sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870
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     getAutoCreate : function()
2915     {
2916         
2917         
2918         return  {
2919             cls :'div' ,
2920             cn : [
2921                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2922                 {
2923                     tag: 'input',
2924                     multiple : 'multiple',
2925                     type : 'file',
2926                     cls : 'd-none  roo-card-upload-selector' 
2927                   
2928                 }
2929                  
2930
2931             ]
2932         };
2933            
2934          
2935     },
2936      
2937    
2938     initEvents : function()
2939     {
2940         
2941         Roo.bootstrap.Button.prototype.initEvents.call(this);
2942         
2943         
2944         
2945         
2946         
2947         this.urlAPI = (window.createObjectURL && window) || 
2948                                 (window.URL && URL.revokeObjectURL && URL) || 
2949                                 (window.webkitURL && webkitURL);
2950                         
2951          
2952          
2953          
2954         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2955         
2956         this.selectorEl.on('change', this.onFileSelected, this);
2957          
2958          
2959        
2960     },
2961     
2962    
2963     onClick : function(e)
2964     {
2965         e.preventDefault();
2966         
2967         if ( this.fireEvent('beforeselect', this) === false) {
2968             return;
2969         }
2970          
2971         this.selectorEl.dom.click();
2972          
2973     },
2974     
2975     onFileSelected : function(e)
2976     {
2977         e.preventDefault();
2978         
2979         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2980             return;
2981         }
2982         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2983         this.selectorEl.dom.reset();
2984         
2985         this.fireEvent('uploaded', this,  files );
2986         
2987     },
2988     
2989        
2990    
2991     
2992     /**
2993      * addCard - add an Attachment to the uploader
2994      * @param data - the data about the image to upload
2995      *
2996      * {
2997           id : 123
2998           title : "Title of file",
2999           is_uploaded : false,
3000           src : "http://.....",
3001           srcfile : { the File upload object },
3002           mimetype : file.type,
3003           preview : false,
3004           is_deleted : 0
3005           .. any other data...
3006         }
3007      *
3008      * 
3009     */
3010      
3011     reset: function()
3012     {
3013          
3014          this.selectorEl
3015     } 
3016     
3017     
3018     
3019     
3020 });
3021  /*
3022  * - LGPL
3023  *
3024  * image
3025  * 
3026  */
3027
3028
3029 /**
3030  * @class Roo.bootstrap.Img
3031  * @extends Roo.bootstrap.Component
3032  * Bootstrap Img class
3033  * @cfg {Boolean} imgResponsive false | true
3034  * @cfg {String} border rounded | circle | thumbnail
3035  * @cfg {String} src image source
3036  * @cfg {String} alt image alternative text
3037  * @cfg {String} href a tag href
3038  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3039  * @cfg {String} xsUrl xs image source
3040  * @cfg {String} smUrl sm image source
3041  * @cfg {String} mdUrl md image source
3042  * @cfg {String} lgUrl lg image source
3043  * 
3044  * @constructor
3045  * Create a new Input
3046  * @param {Object} config The config object
3047  */
3048
3049 Roo.bootstrap.Img = function(config){
3050     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3051     
3052     this.addEvents({
3053         // img events
3054         /**
3055          * @event click
3056          * The img click event for the img.
3057          * @param {Roo.EventObject} e
3058          */
3059         "click" : true
3060     });
3061 };
3062
3063 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3064     
3065     imgResponsive: true,
3066     border: '',
3067     src: 'about:blank',
3068     href: false,
3069     target: false,
3070     xsUrl: '',
3071     smUrl: '',
3072     mdUrl: '',
3073     lgUrl: '',
3074
3075     getAutoCreate : function()
3076     {   
3077         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3078             return this.createSingleImg();
3079         }
3080         
3081         var cfg = {
3082             tag: 'div',
3083             cls: 'roo-image-responsive-group',
3084             cn: []
3085         };
3086         var _this = this;
3087         
3088         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3089             
3090             if(!_this[size + 'Url']){
3091                 return;
3092             }
3093             
3094             var img = {
3095                 tag: 'img',
3096                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3097                 html: _this.html || cfg.html,
3098                 src: _this[size + 'Url']
3099             };
3100             
3101             img.cls += ' roo-image-responsive-' + size;
3102             
3103             var s = ['xs', 'sm', 'md', 'lg'];
3104             
3105             s.splice(s.indexOf(size), 1);
3106             
3107             Roo.each(s, function(ss){
3108                 img.cls += ' hidden-' + ss;
3109             });
3110             
3111             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3112                 cfg.cls += ' img-' + _this.border;
3113             }
3114             
3115             if(_this.alt){
3116                 cfg.alt = _this.alt;
3117             }
3118             
3119             if(_this.href){
3120                 var a = {
3121                     tag: 'a',
3122                     href: _this.href,
3123                     cn: [
3124                         img
3125                     ]
3126                 };
3127
3128                 if(this.target){
3129                     a.target = _this.target;
3130                 }
3131             }
3132             
3133             cfg.cn.push((_this.href) ? a : img);
3134             
3135         });
3136         
3137         return cfg;
3138     },
3139     
3140     createSingleImg : function()
3141     {
3142         var cfg = {
3143             tag: 'img',
3144             cls: (this.imgResponsive) ? 'img-responsive' : '',
3145             html : null,
3146             src : 'about:blank'  // just incase src get's set to undefined?!?
3147         };
3148         
3149         cfg.html = this.html || cfg.html;
3150         
3151         cfg.src = this.src || cfg.src;
3152         
3153         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3154             cfg.cls += ' img-' + this.border;
3155         }
3156         
3157         if(this.alt){
3158             cfg.alt = this.alt;
3159         }
3160         
3161         if(this.href){
3162             var a = {
3163                 tag: 'a',
3164                 href: this.href,
3165                 cn: [
3166                     cfg
3167                 ]
3168             };
3169             
3170             if(this.target){
3171                 a.target = this.target;
3172             }
3173             
3174         }
3175         
3176         return (this.href) ? a : cfg;
3177     },
3178     
3179     initEvents: function() 
3180     {
3181         if(!this.href){
3182             this.el.on('click', this.onClick, this);
3183         }
3184         
3185     },
3186     
3187     onClick : function(e)
3188     {
3189         Roo.log('img onclick');
3190         this.fireEvent('click', this, e);
3191     },
3192     /**
3193      * Sets the url of the image - used to update it
3194      * @param {String} url the url of the image
3195      */
3196     
3197     setSrc : function(url)
3198     {
3199         this.src =  url;
3200         
3201         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3202             this.el.dom.src =  url;
3203             return;
3204         }
3205         
3206         this.el.select('img', true).first().dom.src =  url;
3207     }
3208     
3209     
3210    
3211 });
3212
3213  /*
3214  * - LGPL
3215  *
3216  * image
3217  * 
3218  */
3219
3220
3221 /**
3222  * @class Roo.bootstrap.Link
3223  * @extends Roo.bootstrap.Component
3224  * Bootstrap Link Class
3225  * @cfg {String} alt image alternative text
3226  * @cfg {String} href a tag href
3227  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3228  * @cfg {String} html the content of the link.
3229  * @cfg {String} anchor name for the anchor link
3230  * @cfg {String} fa - favicon
3231
3232  * @cfg {Boolean} preventDefault (true | false) default false
3233
3234  * 
3235  * @constructor
3236  * Create a new Input
3237  * @param {Object} config The config object
3238  */
3239
3240 Roo.bootstrap.Link = function(config){
3241     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3242     
3243     this.addEvents({
3244         // img events
3245         /**
3246          * @event click
3247          * The img click event for the img.
3248          * @param {Roo.EventObject} e
3249          */
3250         "click" : true
3251     });
3252 };
3253
3254 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3255     
3256     href: false,
3257     target: false,
3258     preventDefault: false,
3259     anchor : false,
3260     alt : false,
3261     fa: false,
3262
3263
3264     getAutoCreate : function()
3265     {
3266         var html = this.html || '';
3267         
3268         if (this.fa !== false) {
3269             html = '<i class="fa fa-' + this.fa + '"></i>';
3270         }
3271         var cfg = {
3272             tag: 'a'
3273         };
3274         // anchor's do not require html/href...
3275         if (this.anchor === false) {
3276             cfg.html = html;
3277             cfg.href = this.href || '#';
3278         } else {
3279             cfg.name = this.anchor;
3280             if (this.html !== false || this.fa !== false) {
3281                 cfg.html = html;
3282             }
3283             if (this.href !== false) {
3284                 cfg.href = this.href;
3285             }
3286         }
3287         
3288         if(this.alt !== false){
3289             cfg.alt = this.alt;
3290         }
3291         
3292         
3293         if(this.target !== false) {
3294             cfg.target = this.target;
3295         }
3296         
3297         return cfg;
3298     },
3299     
3300     initEvents: function() {
3301         
3302         if(!this.href || this.preventDefault){
3303             this.el.on('click', this.onClick, this);
3304         }
3305     },
3306     
3307     onClick : function(e)
3308     {
3309         if(this.preventDefault){
3310             e.preventDefault();
3311         }
3312         //Roo.log('img onclick');
3313         this.fireEvent('click', this, e);
3314     }
3315    
3316 });
3317
3318  /*
3319  * - LGPL
3320  *
3321  * header
3322  * 
3323  */
3324
3325 /**
3326  * @class Roo.bootstrap.Header
3327  * @extends Roo.bootstrap.Component
3328  * Bootstrap Header class
3329  * @cfg {String} html content of header
3330  * @cfg {Number} level (1|2|3|4|5|6) default 1
3331  * 
3332  * @constructor
3333  * Create a new Header
3334  * @param {Object} config The config object
3335  */
3336
3337
3338 Roo.bootstrap.Header  = function(config){
3339     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3340 };
3341
3342 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3343     
3344     //href : false,
3345     html : false,
3346     level : 1,
3347     
3348     
3349     
3350     getAutoCreate : function(){
3351         
3352         
3353         
3354         var cfg = {
3355             tag: 'h' + (1 *this.level),
3356             html: this.html || ''
3357         } ;
3358         
3359         return cfg;
3360     }
3361    
3362 });
3363
3364  
3365
3366  /*
3367  * Based on:
3368  * Ext JS Library 1.1.1
3369  * Copyright(c) 2006-2007, Ext JS, LLC.
3370  *
3371  * Originally Released Under LGPL - original licence link has changed is not relivant.
3372  *
3373  * Fork - LGPL
3374  * <script type="text/javascript">
3375  */
3376  
3377 /**
3378  * @class Roo.bootstrap.MenuMgr
3379  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3380  * @singleton
3381  */
3382 Roo.bootstrap.MenuMgr = function(){
3383    var menus, active, groups = {}, attached = false, lastShow = new Date();
3384
3385    // private - called when first menu is created
3386    function init(){
3387        menus = {};
3388        active = new Roo.util.MixedCollection();
3389        Roo.get(document).addKeyListener(27, function(){
3390            if(active.length > 0){
3391                hideAll();
3392            }
3393        });
3394    }
3395
3396    // private
3397    function hideAll(){
3398        if(active && active.length > 0){
3399            var c = active.clone();
3400            c.each(function(m){
3401                m.hide();
3402            });
3403        }
3404    }
3405
3406    // private
3407    function onHide(m){
3408        active.remove(m);
3409        if(active.length < 1){
3410            Roo.get(document).un("mouseup", onMouseDown);
3411             
3412            attached = false;
3413        }
3414    }
3415
3416    // private
3417    function onShow(m){
3418        var last = active.last();
3419        lastShow = new Date();
3420        active.add(m);
3421        if(!attached){
3422           Roo.get(document).on("mouseup", onMouseDown);
3423            
3424            attached = true;
3425        }
3426        if(m.parentMenu){
3427           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3428           m.parentMenu.activeChild = m;
3429        }else if(last && last.isVisible()){
3430           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3431        }
3432    }
3433
3434    // private
3435    function onBeforeHide(m){
3436        if(m.activeChild){
3437            m.activeChild.hide();
3438        }
3439        if(m.autoHideTimer){
3440            clearTimeout(m.autoHideTimer);
3441            delete m.autoHideTimer;
3442        }
3443    }
3444
3445    // private
3446    function onBeforeShow(m){
3447        var pm = m.parentMenu;
3448        if(!pm && !m.allowOtherMenus){
3449            hideAll();
3450        }else if(pm && pm.activeChild && active != m){
3451            pm.activeChild.hide();
3452        }
3453    }
3454
3455    // private this should really trigger on mouseup..
3456    function onMouseDown(e){
3457         Roo.log("on Mouse Up");
3458         
3459         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3460             Roo.log("MenuManager hideAll");
3461             hideAll();
3462             e.stopEvent();
3463         }
3464         
3465         
3466    }
3467
3468    // private
3469    function onBeforeCheck(mi, state){
3470        if(state){
3471            var g = groups[mi.group];
3472            for(var i = 0, l = g.length; i < l; i++){
3473                if(g[i] != mi){
3474                    g[i].setChecked(false);
3475                }
3476            }
3477        }
3478    }
3479
3480    return {
3481
3482        /**
3483         * Hides all menus that are currently visible
3484         */
3485        hideAll : function(){
3486             hideAll();  
3487        },
3488
3489        // private
3490        register : function(menu){
3491            if(!menus){
3492                init();
3493            }
3494            menus[menu.id] = menu;
3495            menu.on("beforehide", onBeforeHide);
3496            menu.on("hide", onHide);
3497            menu.on("beforeshow", onBeforeShow);
3498            menu.on("show", onShow);
3499            var g = menu.group;
3500            if(g && menu.events["checkchange"]){
3501                if(!groups[g]){
3502                    groups[g] = [];
3503                }
3504                groups[g].push(menu);
3505                menu.on("checkchange", onCheck);
3506            }
3507        },
3508
3509         /**
3510          * Returns a {@link Roo.menu.Menu} object
3511          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3512          * be used to generate and return a new Menu instance.
3513          */
3514        get : function(menu){
3515            if(typeof menu == "string"){ // menu id
3516                return menus[menu];
3517            }else if(menu.events){  // menu instance
3518                return menu;
3519            }
3520            /*else if(typeof menu.length == 'number'){ // array of menu items?
3521                return new Roo.bootstrap.Menu({items:menu});
3522            }else{ // otherwise, must be a config
3523                return new Roo.bootstrap.Menu(menu);
3524            }
3525            */
3526            return false;
3527        },
3528
3529        // private
3530        unregister : function(menu){
3531            delete menus[menu.id];
3532            menu.un("beforehide", onBeforeHide);
3533            menu.un("hide", onHide);
3534            menu.un("beforeshow", onBeforeShow);
3535            menu.un("show", onShow);
3536            var g = menu.group;
3537            if(g && menu.events["checkchange"]){
3538                groups[g].remove(menu);
3539                menu.un("checkchange", onCheck);
3540            }
3541        },
3542
3543        // private
3544        registerCheckable : function(menuItem){
3545            var g = menuItem.group;
3546            if(g){
3547                if(!groups[g]){
3548                    groups[g] = [];
3549                }
3550                groups[g].push(menuItem);
3551                menuItem.on("beforecheckchange", onBeforeCheck);
3552            }
3553        },
3554
3555        // private
3556        unregisterCheckable : function(menuItem){
3557            var g = menuItem.group;
3558            if(g){
3559                groups[g].remove(menuItem);
3560                menuItem.un("beforecheckchange", onBeforeCheck);
3561            }
3562        }
3563    };
3564 }();/*
3565  * - LGPL
3566  *
3567  * menu
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.Menu
3573  * @extends Roo.bootstrap.Component
3574  * Bootstrap Menu class - container for MenuItems
3575  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3576  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3577  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3578  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3579  * 
3580  * @constructor
3581  * Create a new Menu
3582  * @param {Object} config The config object
3583  */
3584
3585
3586 Roo.bootstrap.Menu = function(config){
3587     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3588     if (this.registerMenu && this.type != 'treeview')  {
3589         Roo.bootstrap.MenuMgr.register(this);
3590     }
3591     
3592     
3593     this.addEvents({
3594         /**
3595          * @event beforeshow
3596          * Fires before this menu is displayed (return false to block)
3597          * @param {Roo.menu.Menu} this
3598          */
3599         beforeshow : true,
3600         /**
3601          * @event beforehide
3602          * Fires before this menu is hidden (return false to block)
3603          * @param {Roo.menu.Menu} this
3604          */
3605         beforehide : true,
3606         /**
3607          * @event show
3608          * Fires after this menu is displayed
3609          * @param {Roo.menu.Menu} this
3610          */
3611         show : true,
3612         /**
3613          * @event hide
3614          * Fires after this menu is hidden
3615          * @param {Roo.menu.Menu} this
3616          */
3617         hide : true,
3618         /**
3619          * @event click
3620          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3621          * @param {Roo.menu.Menu} this
3622          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3623          * @param {Roo.EventObject} e
3624          */
3625         click : true,
3626         /**
3627          * @event mouseover
3628          * Fires when the mouse is hovering over this menu
3629          * @param {Roo.menu.Menu} this
3630          * @param {Roo.EventObject} e
3631          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3632          */
3633         mouseover : true,
3634         /**
3635          * @event mouseout
3636          * Fires when the mouse exits this menu
3637          * @param {Roo.menu.Menu} this
3638          * @param {Roo.EventObject} e
3639          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3640          */
3641         mouseout : true,
3642         /**
3643          * @event itemclick
3644          * Fires when a menu item contained in this menu is clicked
3645          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3646          * @param {Roo.EventObject} e
3647          */
3648         itemclick: true
3649     });
3650     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3651 };
3652
3653 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3654     
3655    /// html : false,
3656     //align : '',
3657     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3658     type: false,
3659     /**
3660      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3661      */
3662     registerMenu : true,
3663     
3664     menuItems :false, // stores the menu items..
3665     
3666     hidden:true,
3667         
3668     parentMenu : false,
3669     
3670     stopEvent : true,
3671     
3672     isLink : false,
3673     
3674     container_method : 'getDocumentBody',
3675     
3676     
3677     getChildContainer : function() {
3678         return this.el;  
3679     },
3680     
3681     getAutoCreate : function(){
3682          
3683         //if (['right'].indexOf(this.align)!==-1) {
3684         //    cfg.cn[1].cls += ' pull-right'
3685         //}
3686         
3687         
3688         var cfg = {
3689             tag : 'ul',
3690             cls : 'dropdown-menu' ,
3691             style : 'z-index:1000'
3692             
3693         };
3694         
3695         if (this.type === 'submenu') {
3696             cfg.cls = 'submenu active';
3697         }
3698         if (this.type === 'treeview') {
3699             cfg.cls = 'treeview-menu';
3700         }
3701         
3702         return cfg;
3703     },
3704     initEvents : function() {
3705         
3706        // Roo.log("ADD event");
3707        // Roo.log(this.triggerEl.dom);
3708         
3709         this.triggerEl.on('click', this.onTriggerClick, this);
3710         
3711         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3712         
3713         
3714         if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3715             // dropdown toggle on the 'a' in BS4?
3716             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3717         } else if (!this.triggerEl.hasClass('no-dropdown-toggle')) {
3718             this.triggerEl.addClass('dropdown-toggle');
3719         }
3720         if (Roo.isTouch) {
3721             this.el.on('touchstart'  , this.onTouch, this);
3722         }
3723         this.el.on('click' , this.onClick, this);
3724
3725         this.el.on("mouseover", this.onMouseOver, this);
3726         this.el.on("mouseout", this.onMouseOut, this);
3727         
3728     },
3729     
3730     findTargetItem : function(e)
3731     {
3732         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3733         if(!t){
3734             return false;
3735         }
3736         //Roo.log(t);         Roo.log(t.id);
3737         if(t && t.id){
3738             //Roo.log(this.menuitems);
3739             return this.menuitems.get(t.id);
3740             
3741             //return this.items.get(t.menuItemId);
3742         }
3743         
3744         return false;
3745     },
3746     
3747     onTouch : function(e) 
3748     {
3749         Roo.log("menu.onTouch");
3750         //e.stopEvent(); this make the user popdown broken
3751         this.onClick(e);
3752     },
3753     
3754     onClick : function(e)
3755     {
3756         Roo.log("menu.onClick");
3757         
3758         var t = this.findTargetItem(e);
3759         if(!t || t.isContainer){
3760             return;
3761         }
3762         Roo.log(e);
3763         /*
3764         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3765             if(t == this.activeItem && t.shouldDeactivate(e)){
3766                 this.activeItem.deactivate();
3767                 delete this.activeItem;
3768                 return;
3769             }
3770             if(t.canActivate){
3771                 this.setActiveItem(t, true);
3772             }
3773             return;
3774             
3775             
3776         }
3777         */
3778        
3779         Roo.log('pass click event');
3780         
3781         t.onClick(e);
3782         
3783         this.fireEvent("click", this, t, e);
3784         
3785         var _this = this;
3786         
3787         if(!t.href.length || t.href == '#'){
3788             (function() { _this.hide(); }).defer(100);
3789         }
3790         
3791     },
3792     
3793     onMouseOver : function(e){
3794         var t  = this.findTargetItem(e);
3795         //Roo.log(t);
3796         //if(t){
3797         //    if(t.canActivate && !t.disabled){
3798         //        this.setActiveItem(t, true);
3799         //    }
3800         //}
3801         
3802         this.fireEvent("mouseover", this, e, t);
3803     },
3804     isVisible : function(){
3805         return !this.hidden;
3806     },
3807     onMouseOut : function(e){
3808         var t  = this.findTargetItem(e);
3809         
3810         //if(t ){
3811         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3812         //        this.activeItem.deactivate();
3813         //        delete this.activeItem;
3814         //    }
3815         //}
3816         this.fireEvent("mouseout", this, e, t);
3817     },
3818     
3819     
3820     /**
3821      * Displays this menu relative to another element
3822      * @param {String/HTMLElement/Roo.Element} element The element to align to
3823      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3824      * the element (defaults to this.defaultAlign)
3825      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3826      */
3827     show : function(el, pos, parentMenu)
3828     {
3829         if (false === this.fireEvent("beforeshow", this)) {
3830             Roo.log("show canceled");
3831             return;
3832         }
3833         this.parentMenu = parentMenu;
3834         if(!this.el){
3835             this.render();
3836         }
3837         
3838         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3839     },
3840      /**
3841      * Displays this menu at a specific xy position
3842      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3843      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3844      */
3845     showAt : function(xy, parentMenu, /* private: */_e){
3846         this.parentMenu = parentMenu;
3847         if(!this.el){
3848             this.render();
3849         }
3850         if(_e !== false){
3851             this.fireEvent("beforeshow", this);
3852             //xy = this.el.adjustForConstraints(xy);
3853         }
3854         
3855         //this.el.show();
3856         this.hideMenuItems();
3857         this.hidden = false;
3858         this.triggerEl.addClass('open');
3859         this.el.addClass('show');
3860         
3861         // reassign x when hitting right
3862         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3863             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3864         }
3865         
3866         // reassign y when hitting bottom
3867         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3868             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3869         }
3870         
3871         // but the list may align on trigger left or trigger top... should it be a properity?
3872         
3873         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3874             this.el.setXY(xy);
3875         }
3876         
3877         this.focus();
3878         this.fireEvent("show", this);
3879     },
3880     
3881     focus : function(){
3882         return;
3883         if(!this.hidden){
3884             this.doFocus.defer(50, this);
3885         }
3886     },
3887
3888     doFocus : function(){
3889         if(!this.hidden){
3890             this.focusEl.focus();
3891         }
3892     },
3893
3894     /**
3895      * Hides this menu and optionally all parent menus
3896      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3897      */
3898     hide : function(deep)
3899     {
3900         if (false === this.fireEvent("beforehide", this)) {
3901             Roo.log("hide canceled");
3902             return;
3903         }
3904         this.hideMenuItems();
3905         if(this.el && this.isVisible()){
3906            
3907             if(this.activeItem){
3908                 this.activeItem.deactivate();
3909                 this.activeItem = null;
3910             }
3911             this.triggerEl.removeClass('open');;
3912             this.el.removeClass('show');
3913             this.hidden = true;
3914             this.fireEvent("hide", this);
3915         }
3916         if(deep === true && this.parentMenu){
3917             this.parentMenu.hide(true);
3918         }
3919     },
3920     
3921     onTriggerClick : function(e)
3922     {
3923         Roo.log('trigger click');
3924         
3925         var target = e.getTarget();
3926         
3927         Roo.log(target.nodeName.toLowerCase());
3928         
3929         if(target.nodeName.toLowerCase() === 'i'){
3930             e.preventDefault();
3931         }
3932         
3933     },
3934     
3935     onTriggerPress  : function(e)
3936     {
3937         Roo.log('trigger press');
3938         //Roo.log(e.getTarget());
3939        // Roo.log(this.triggerEl.dom);
3940        
3941         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3942         var pel = Roo.get(e.getTarget());
3943         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3944             Roo.log('is treeview or dropdown?');
3945             return;
3946         }
3947         
3948         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3949             return;
3950         }
3951         
3952         if (this.isVisible()) {
3953             Roo.log('hide');
3954             this.hide();
3955         } else {
3956             Roo.log('show');
3957             this.show(this.triggerEl, '?', false);
3958         }
3959         
3960         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3961             e.stopEvent();
3962         }
3963         
3964     },
3965        
3966     
3967     hideMenuItems : function()
3968     {
3969         Roo.log("hide Menu Items");
3970         if (!this.el) { 
3971             return;
3972         }
3973         
3974         this.el.select('.open',true).each(function(aa) {
3975             
3976             aa.removeClass('open');
3977          
3978         });
3979     },
3980     addxtypeChild : function (tree, cntr) {
3981         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3982           
3983         this.menuitems.add(comp);
3984         return comp;
3985
3986     },
3987     getEl : function()
3988     {
3989         Roo.log(this.el);
3990         return this.el;
3991     },
3992     
3993     clear : function()
3994     {
3995         this.getEl().dom.innerHTML = '';
3996         this.menuitems.clear();
3997     }
3998 });
3999
4000  
4001  /*
4002  * - LGPL
4003  *
4004  * menu item
4005  * 
4006  */
4007
4008
4009 /**
4010  * @class Roo.bootstrap.MenuItem
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap MenuItem class
4013  * @cfg {String} html the menu label
4014  * @cfg {String} href the link
4015  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4016  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4017  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4018  * @cfg {String} fa favicon to show on left of menu item.
4019  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4020  * 
4021  * 
4022  * @constructor
4023  * Create a new MenuItem
4024  * @param {Object} config The config object
4025  */
4026
4027
4028 Roo.bootstrap.MenuItem = function(config){
4029     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4030     this.addEvents({
4031         // raw events
4032         /**
4033          * @event click
4034          * The raw click event for the entire grid.
4035          * @param {Roo.bootstrap.MenuItem} this
4036          * @param {Roo.EventObject} e
4037          */
4038         "click" : true
4039     });
4040 };
4041
4042 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4043     
4044     href : false,
4045     html : false,
4046     preventDefault: false,
4047     isContainer : false,
4048     active : false,
4049     fa: false,
4050     
4051     getAutoCreate : function(){
4052         
4053         if(this.isContainer){
4054             return {
4055                 tag: 'li',
4056                 cls: 'dropdown-menu-item '
4057             };
4058         }
4059         var ctag = {
4060             tag: 'span',
4061             html: 'Link'
4062         };
4063         
4064         var anc = {
4065             tag : 'a',
4066             cls : 'dropdown-item',
4067             href : '#',
4068             cn : [  ]
4069         };
4070         
4071         if (this.fa !== false) {
4072             anc.cn.push({
4073                 tag : 'i',
4074                 cls : 'fa fa-' + this.fa
4075             });
4076         }
4077         
4078         anc.cn.push(ctag);
4079         
4080         
4081         var cfg= {
4082             tag: 'li',
4083             cls: 'dropdown-menu-item',
4084             cn: [ anc ]
4085         };
4086         if (this.parent().type == 'treeview') {
4087             cfg.cls = 'treeview-menu';
4088         }
4089         if (this.active) {
4090             cfg.cls += ' active';
4091         }
4092         
4093         
4094         
4095         anc.href = this.href || cfg.cn[0].href ;
4096         ctag.html = this.html || cfg.cn[0].html ;
4097         return cfg;
4098     },
4099     
4100     initEvents: function()
4101     {
4102         if (this.parent().type == 'treeview') {
4103             this.el.select('a').on('click', this.onClick, this);
4104         }
4105         
4106         if (this.menu) {
4107             this.menu.parentType = this.xtype;
4108             this.menu.triggerEl = this.el;
4109             this.menu = this.addxtype(Roo.apply({}, this.menu));
4110         }
4111         
4112     },
4113     onClick : function(e)
4114     {
4115         Roo.log('item on click ');
4116         
4117         if(this.preventDefault){
4118             e.preventDefault();
4119         }
4120         //this.parent().hideMenuItems();
4121         
4122         this.fireEvent('click', this, e);
4123     },
4124     getEl : function()
4125     {
4126         return this.el;
4127     } 
4128 });
4129
4130  
4131
4132  /*
4133  * - LGPL
4134  *
4135  * menu separator
4136  * 
4137  */
4138
4139
4140 /**
4141  * @class Roo.bootstrap.MenuSeparator
4142  * @extends Roo.bootstrap.Component
4143  * Bootstrap MenuSeparator class
4144  * 
4145  * @constructor
4146  * Create a new MenuItem
4147  * @param {Object} config The config object
4148  */
4149
4150
4151 Roo.bootstrap.MenuSeparator = function(config){
4152     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4153 };
4154
4155 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4156     
4157     getAutoCreate : function(){
4158         var cfg = {
4159             cls: 'divider',
4160             tag : 'li'
4161         };
4162         
4163         return cfg;
4164     }
4165    
4166 });
4167
4168  
4169
4170  
4171 /*
4172 * Licence: LGPL
4173 */
4174
4175 /**
4176  * @class Roo.bootstrap.Modal
4177  * @extends Roo.bootstrap.Component
4178  * Bootstrap Modal class
4179  * @cfg {String} title Title of dialog
4180  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4181  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4182  * @cfg {Boolean} specificTitle default false
4183  * @cfg {Array} buttons Array of buttons or standard button set..
4184  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4185  * @cfg {Boolean} animate default true
4186  * @cfg {Boolean} allow_close default true
4187  * @cfg {Boolean} fitwindow default false
4188  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4189  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4190  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4191  * @cfg {String} size (sm|lg|xl) default empty
4192  * @cfg {Number} max_width set the max width of modal
4193  * @cfg {Boolean} editableTitle can the title be edited
4194
4195  *
4196  *
4197  * @constructor
4198  * Create a new Modal Dialog
4199  * @param {Object} config The config object
4200  */
4201
4202 Roo.bootstrap.Modal = function(config){
4203     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4204     this.addEvents({
4205         // raw events
4206         /**
4207          * @event btnclick
4208          * The raw btnclick event for the button
4209          * @param {Roo.EventObject} e
4210          */
4211         "btnclick" : true,
4212         /**
4213          * @event resize
4214          * Fire when dialog resize
4215          * @param {Roo.bootstrap.Modal} this
4216          * @param {Roo.EventObject} e
4217          */
4218         "resize" : true,
4219         /**
4220          * @event titlechanged
4221          * Fire when the editable title has been changed
4222          * @param {Roo.bootstrap.Modal} this
4223          * @param {Roo.EventObject} value
4224          */
4225         "titlechanged" : true 
4226         
4227     });
4228     this.buttons = this.buttons || [];
4229
4230     if (this.tmpl) {
4231         this.tmpl = Roo.factory(this.tmpl);
4232     }
4233
4234 };
4235
4236 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4237
4238     title : 'test dialog',
4239
4240     buttons : false,
4241
4242     // set on load...
4243
4244     html: false,
4245
4246     tmp: false,
4247
4248     specificTitle: false,
4249
4250     buttonPosition: 'right',
4251
4252     allow_close : true,
4253
4254     animate : true,
4255
4256     fitwindow: false,
4257     
4258      // private
4259     dialogEl: false,
4260     bodyEl:  false,
4261     footerEl:  false,
4262     titleEl:  false,
4263     closeEl:  false,
4264
4265     size: '',
4266     
4267     max_width: 0,
4268     
4269     max_height: 0,
4270     
4271     fit_content: false,
4272     editableTitle  : false,
4273
4274     onRender : function(ct, position)
4275     {
4276         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4277
4278         if(!this.el){
4279             var cfg = Roo.apply({},  this.getAutoCreate());
4280             cfg.id = Roo.id();
4281             //if(!cfg.name){
4282             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4283             //}
4284             //if (!cfg.name.length) {
4285             //    delete cfg.name;
4286            // }
4287             if (this.cls) {
4288                 cfg.cls += ' ' + this.cls;
4289             }
4290             if (this.style) {
4291                 cfg.style = this.style;
4292             }
4293             this.el = Roo.get(document.body).createChild(cfg, position);
4294         }
4295         //var type = this.el.dom.type;
4296
4297
4298         if(this.tabIndex !== undefined){
4299             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4300         }
4301
4302         this.dialogEl = this.el.select('.modal-dialog',true).first();
4303         this.bodyEl = this.el.select('.modal-body',true).first();
4304         this.closeEl = this.el.select('.modal-header .close', true).first();
4305         this.headerEl = this.el.select('.modal-header',true).first();
4306         this.titleEl = this.el.select('.modal-title',true).first();
4307         this.footerEl = this.el.select('.modal-footer',true).first();
4308
4309         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4310         
4311         //this.el.addClass("x-dlg-modal");
4312
4313         if (this.buttons.length) {
4314             Roo.each(this.buttons, function(bb) {
4315                 var b = Roo.apply({}, bb);
4316                 b.xns = b.xns || Roo.bootstrap;
4317                 b.xtype = b.xtype || 'Button';
4318                 if (typeof(b.listeners) == 'undefined') {
4319                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4320                 }
4321
4322                 var btn = Roo.factory(b);
4323
4324                 btn.render(this.getButtonContainer());
4325
4326             },this);
4327         }
4328         // render the children.
4329         var nitems = [];
4330
4331         if(typeof(this.items) != 'undefined'){
4332             var items = this.items;
4333             delete this.items;
4334
4335             for(var i =0;i < items.length;i++) {
4336                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4337             }
4338         }
4339
4340         this.items = nitems;
4341
4342         // where are these used - they used to be body/close/footer
4343
4344
4345         this.initEvents();
4346         //this.el.addClass([this.fieldClass, this.cls]);
4347
4348     },
4349
4350     getAutoCreate : function()
4351     {
4352         // we will default to modal-body-overflow - might need to remove or make optional later.
4353         var bdy = {
4354                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4355                 html : this.html || ''
4356         };
4357
4358         var title = {
4359             tag: 'h5',
4360             cls : 'modal-title',
4361             html : this.title
4362         };
4363
4364         if(this.specificTitle){ // WTF is this?
4365             title = this.title;
4366         }
4367
4368         var header = [];
4369         if (this.allow_close && Roo.bootstrap.version == 3) {
4370             header.push({
4371                 tag: 'button',
4372                 cls : 'close',
4373                 html : '&times'
4374             });
4375         }
4376
4377         header.push(title);
4378
4379         if (this.editableTitle) {
4380             header.push({
4381                 cls: 'form-control roo-editable-title d-none',
4382                 tag: 'input',
4383                 type: 'text'
4384             });
4385         }
4386         
4387         if (this.allow_close && Roo.bootstrap.version == 4) {
4388             header.push({
4389                 tag: 'button',
4390                 cls : 'close',
4391                 html : '&times'
4392             });
4393         }
4394         
4395         var size = '';
4396
4397         if(this.size.length){
4398             size = 'modal-' + this.size;
4399         }
4400         
4401         var footer = Roo.bootstrap.version == 3 ?
4402             {
4403                 cls : 'modal-footer',
4404                 cn : [
4405                     {
4406                         tag: 'div',
4407                         cls: 'btn-' + this.buttonPosition
4408                     }
4409                 ]
4410
4411             } :
4412             {  // BS4 uses mr-auto on left buttons....
4413                 cls : 'modal-footer'
4414             };
4415
4416             
4417
4418         
4419         
4420         var modal = {
4421             cls: "modal",
4422              cn : [
4423                 {
4424                     cls: "modal-dialog " + size,
4425                     cn : [
4426                         {
4427                             cls : "modal-content",
4428                             cn : [
4429                                 {
4430                                     cls : 'modal-header',
4431                                     cn : header
4432                                 },
4433                                 bdy,
4434                                 footer
4435                             ]
4436
4437                         }
4438                     ]
4439
4440                 }
4441             ]
4442         };
4443
4444         if(this.animate){
4445             modal.cls += ' fade';
4446         }
4447
4448         return modal;
4449
4450     },
4451     getChildContainer : function() {
4452
4453          return this.bodyEl;
4454
4455     },
4456     getButtonContainer : function() {
4457         
4458          return Roo.bootstrap.version == 4 ?
4459             this.el.select('.modal-footer',true).first()
4460             : this.el.select('.modal-footer div',true).first();
4461
4462     },
4463     initEvents : function()
4464     {
4465         if (this.allow_close) {
4466             this.closeEl.on('click', this.hide, this);
4467         }
4468         Roo.EventManager.onWindowResize(this.resize, this, true);
4469         if (this.editableTitle) {
4470             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4471             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4472             this.headerEditEl.on('keyup', function(e) {
4473                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4474                         this.toggleHeaderInput(false)
4475                     }
4476                 }, this);
4477             this.headerEditEl.on('blur', function(e) {
4478                 this.toggleHeaderInput(false)
4479             },this);
4480         }
4481
4482     },
4483   
4484
4485     resize : function()
4486     {
4487         this.maskEl.setSize(
4488             Roo.lib.Dom.getViewWidth(true),
4489             Roo.lib.Dom.getViewHeight(true)
4490         );
4491         
4492         if (this.fitwindow) {
4493             
4494            this.dialogEl.setStyle( { 'max-width' : '100%' });
4495             this.setSize(
4496                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4497                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4498             );
4499             return;
4500         }
4501         
4502         if(this.max_width !== 0) {
4503             
4504             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4505             
4506             if(this.height) {
4507                 this.setSize(w, this.height);
4508                 return;
4509             }
4510             
4511             if(this.max_height) {
4512                 this.setSize(w,Math.min(
4513                     this.max_height,
4514                     Roo.lib.Dom.getViewportHeight(true) - 60
4515                 ));
4516                 
4517                 return;
4518             }
4519             
4520             if(!this.fit_content) {
4521                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4522                 return;
4523             }
4524             
4525             this.setSize(w, Math.min(
4526                 60 +
4527                 this.headerEl.getHeight() + 
4528                 this.footerEl.getHeight() + 
4529                 this.getChildHeight(this.bodyEl.dom.childNodes),
4530                 Roo.lib.Dom.getViewportHeight(true) - 60)
4531             );
4532         }
4533         
4534     },
4535
4536     setSize : function(w,h)
4537     {
4538         if (!w && !h) {
4539             return;
4540         }
4541         
4542         this.resizeTo(w,h);
4543     },
4544
4545     show : function() {
4546
4547         if (!this.rendered) {
4548             this.render();
4549         }
4550         this.toggleHeaderInput(false);
4551         //this.el.setStyle('display', 'block');
4552         this.el.removeClass('hideing');
4553         this.el.dom.style.display='block';
4554         
4555         Roo.get(document.body).addClass('modal-open');
4556  
4557         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4558             
4559             (function(){
4560                 this.el.addClass('show');
4561                 this.el.addClass('in');
4562             }).defer(50, this);
4563         }else{
4564             this.el.addClass('show');
4565             this.el.addClass('in');
4566         }
4567
4568         // not sure how we can show data in here..
4569         //if (this.tmpl) {
4570         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4571         //}
4572
4573         Roo.get(document.body).addClass("x-body-masked");
4574         
4575         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4576         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4577         this.maskEl.dom.style.display = 'block';
4578         this.maskEl.addClass('show');
4579         
4580         
4581         this.resize();
4582         
4583         this.fireEvent('show', this);
4584
4585         // set zindex here - otherwise it appears to be ignored...
4586         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4587
4588         (function () {
4589             this.items.forEach( function(e) {
4590                 e.layout ? e.layout() : false;
4591
4592             });
4593         }).defer(100,this);
4594
4595     },
4596     hide : function()
4597     {
4598         if(this.fireEvent("beforehide", this) !== false){
4599             
4600             this.maskEl.removeClass('show');
4601             
4602             this.maskEl.dom.style.display = '';
4603             Roo.get(document.body).removeClass("x-body-masked");
4604             this.el.removeClass('in');
4605             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4606
4607             if(this.animate){ // why
4608                 this.el.addClass('hideing');
4609                 this.el.removeClass('show');
4610                 (function(){
4611                     if (!this.el.hasClass('hideing')) {
4612                         return; // it's been shown again...
4613                     }
4614                     
4615                     this.el.dom.style.display='';
4616
4617                     Roo.get(document.body).removeClass('modal-open');
4618                     this.el.removeClass('hideing');
4619                 }).defer(150,this);
4620                 
4621             }else{
4622                 this.el.removeClass('show');
4623                 this.el.dom.style.display='';
4624                 Roo.get(document.body).removeClass('modal-open');
4625
4626             }
4627             this.fireEvent('hide', this);
4628         }
4629     },
4630     isVisible : function()
4631     {
4632         
4633         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4634         
4635     },
4636
4637     addButton : function(str, cb)
4638     {
4639
4640
4641         var b = Roo.apply({}, { html : str } );
4642         b.xns = b.xns || Roo.bootstrap;
4643         b.xtype = b.xtype || 'Button';
4644         if (typeof(b.listeners) == 'undefined') {
4645             b.listeners = { click : cb.createDelegate(this)  };
4646         }
4647
4648         var btn = Roo.factory(b);
4649
4650         btn.render(this.getButtonContainer());
4651
4652         return btn;
4653
4654     },
4655
4656     setDefaultButton : function(btn)
4657     {
4658         //this.el.select('.modal-footer').()
4659     },
4660
4661     resizeTo: function(w,h)
4662     {
4663         this.dialogEl.setWidth(w);
4664         
4665         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4666
4667         this.bodyEl.setHeight(h - diff);
4668         
4669         this.fireEvent('resize', this);
4670     },
4671     
4672     setContentSize  : function(w, h)
4673     {
4674
4675     },
4676     onButtonClick: function(btn,e)
4677     {
4678         //Roo.log([a,b,c]);
4679         this.fireEvent('btnclick', btn.name, e);
4680     },
4681      /**
4682      * Set the title of the Dialog
4683      * @param {String} str new Title
4684      */
4685     setTitle: function(str) {
4686         this.titleEl.dom.innerHTML = str;
4687         this.title = str;
4688     },
4689     /**
4690      * Set the body of the Dialog
4691      * @param {String} str new Title
4692      */
4693     setBody: function(str) {
4694         this.bodyEl.dom.innerHTML = str;
4695     },
4696     /**
4697      * Set the body of the Dialog using the template
4698      * @param {Obj} data - apply this data to the template and replace the body contents.
4699      */
4700     applyBody: function(obj)
4701     {
4702         if (!this.tmpl) {
4703             Roo.log("Error - using apply Body without a template");
4704             //code
4705         }
4706         this.tmpl.overwrite(this.bodyEl, obj);
4707     },
4708     
4709     getChildHeight : function(child_nodes)
4710     {
4711         if(
4712             !child_nodes ||
4713             child_nodes.length == 0
4714         ) {
4715             return 0;
4716         }
4717         
4718         var child_height = 0;
4719         
4720         for(var i = 0; i < child_nodes.length; i++) {
4721             
4722             /*
4723             * for modal with tabs...
4724             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4725                 
4726                 var layout_childs = child_nodes[i].childNodes;
4727                 
4728                 for(var j = 0; j < layout_childs.length; j++) {
4729                     
4730                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4731                         
4732                         var layout_body_childs = layout_childs[j].childNodes;
4733                         
4734                         for(var k = 0; k < layout_body_childs.length; k++) {
4735                             
4736                             if(layout_body_childs[k].classList.contains('navbar')) {
4737                                 child_height += layout_body_childs[k].offsetHeight;
4738                                 continue;
4739                             }
4740                             
4741                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4742                                 
4743                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4744                                 
4745                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4746                                     
4747                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4748                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4749                                         continue;
4750                                     }
4751                                     
4752                                 }
4753                                 
4754                             }
4755                             
4756                         }
4757                     }
4758                 }
4759                 continue;
4760             }
4761             */
4762             
4763             child_height += child_nodes[i].offsetHeight;
4764             // Roo.log(child_nodes[i].offsetHeight);
4765         }
4766         
4767         return child_height;
4768     },
4769     toggleHeaderInput : function(is_edit)
4770     {
4771         if (!this.editableTitle) {
4772             return; // not editable.
4773         }
4774         if (is_edit && this.is_header_editing) {
4775             return; // already editing..
4776         }
4777         if (is_edit) {
4778     
4779             this.headerEditEl.dom.value = this.title;
4780             this.headerEditEl.removeClass('d-none');
4781             this.headerEditEl.dom.focus();
4782             this.titleEl.addClass('d-none');
4783             
4784             this.is_header_editing = true;
4785             return
4786         }
4787         // flip back to not editing.
4788         this.title = this.headerEditEl.dom.value;
4789         this.headerEditEl.addClass('d-none');
4790         this.titleEl.removeClass('d-none');
4791         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4792         this.is_header_editing = false;
4793         this.fireEvent('titlechanged', this, this.title);
4794     
4795             
4796         
4797     }
4798
4799 });
4800
4801
4802 Roo.apply(Roo.bootstrap.Modal,  {
4803     /**
4804          * Button config that displays a single OK button
4805          * @type Object
4806          */
4807         OK :  [{
4808             name : 'ok',
4809             weight : 'primary',
4810             html : 'OK'
4811         }],
4812         /**
4813          * Button config that displays Yes and No buttons
4814          * @type Object
4815          */
4816         YESNO : [
4817             {
4818                 name  : 'no',
4819                 html : 'No'
4820             },
4821             {
4822                 name  :'yes',
4823                 weight : 'primary',
4824                 html : 'Yes'
4825             }
4826         ],
4827
4828         /**
4829          * Button config that displays OK and Cancel buttons
4830          * @type Object
4831          */
4832         OKCANCEL : [
4833             {
4834                name : 'cancel',
4835                 html : 'Cancel'
4836             },
4837             {
4838                 name : 'ok',
4839                 weight : 'primary',
4840                 html : 'OK'
4841             }
4842         ],
4843         /**
4844          * Button config that displays Yes, No and Cancel buttons
4845          * @type Object
4846          */
4847         YESNOCANCEL : [
4848             {
4849                 name : 'yes',
4850                 weight : 'primary',
4851                 html : 'Yes'
4852             },
4853             {
4854                 name : 'no',
4855                 html : 'No'
4856             },
4857             {
4858                 name : 'cancel',
4859                 html : 'Cancel'
4860             }
4861         ],
4862         
4863         zIndex : 10001
4864 });
4865
4866 /*
4867  * - LGPL
4868  *
4869  * messagebox - can be used as a replace
4870  * 
4871  */
4872 /**
4873  * @class Roo.MessageBox
4874  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4875  * Example usage:
4876  *<pre><code>
4877 // Basic alert:
4878 Roo.Msg.alert('Status', 'Changes saved successfully.');
4879
4880 // Prompt for user data:
4881 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4882     if (btn == 'ok'){
4883         // process text value...
4884     }
4885 });
4886
4887 // Show a dialog using config options:
4888 Roo.Msg.show({
4889    title:'Save Changes?',
4890    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4891    buttons: Roo.Msg.YESNOCANCEL,
4892    fn: processResult,
4893    animEl: 'elId'
4894 });
4895 </code></pre>
4896  * @singleton
4897  */
4898 Roo.bootstrap.MessageBox = function(){
4899     var dlg, opt, mask, waitTimer;
4900     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4901     var buttons, activeTextEl, bwidth;
4902
4903     
4904     // private
4905     var handleButton = function(button){
4906         dlg.hide();
4907         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4908     };
4909
4910     // private
4911     var handleHide = function(){
4912         if(opt && opt.cls){
4913             dlg.el.removeClass(opt.cls);
4914         }
4915         //if(waitTimer){
4916         //    Roo.TaskMgr.stop(waitTimer);
4917         //    waitTimer = null;
4918         //}
4919     };
4920
4921     // private
4922     var updateButtons = function(b){
4923         var width = 0;
4924         if(!b){
4925             buttons["ok"].hide();
4926             buttons["cancel"].hide();
4927             buttons["yes"].hide();
4928             buttons["no"].hide();
4929             dlg.footerEl.hide();
4930             
4931             return width;
4932         }
4933         dlg.footerEl.show();
4934         for(var k in buttons){
4935             if(typeof buttons[k] != "function"){
4936                 if(b[k]){
4937                     buttons[k].show();
4938                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4939                     width += buttons[k].el.getWidth()+15;
4940                 }else{
4941                     buttons[k].hide();
4942                 }
4943             }
4944         }
4945         return width;
4946     };
4947
4948     // private
4949     var handleEsc = function(d, k, e){
4950         if(opt && opt.closable !== false){
4951             dlg.hide();
4952         }
4953         if(e){
4954             e.stopEvent();
4955         }
4956     };
4957
4958     return {
4959         /**
4960          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4961          * @return {Roo.BasicDialog} The BasicDialog element
4962          */
4963         getDialog : function(){
4964            if(!dlg){
4965                 dlg = new Roo.bootstrap.Modal( {
4966                     //draggable: true,
4967                     //resizable:false,
4968                     //constraintoviewport:false,
4969                     //fixedcenter:true,
4970                     //collapsible : false,
4971                     //shim:true,
4972                     //modal: true,
4973                 //    width: 'auto',
4974                   //  height:100,
4975                     //buttonAlign:"center",
4976                     closeClick : function(){
4977                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4978                             handleButton("no");
4979                         }else{
4980                             handleButton("cancel");
4981                         }
4982                     }
4983                 });
4984                 dlg.render();
4985                 dlg.on("hide", handleHide);
4986                 mask = dlg.mask;
4987                 //dlg.addKeyListener(27, handleEsc);
4988                 buttons = {};
4989                 this.buttons = buttons;
4990                 var bt = this.buttonText;
4991                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4992                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4993                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4994                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4995                 //Roo.log(buttons);
4996                 bodyEl = dlg.bodyEl.createChild({
4997
4998                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4999                         '<textarea class="roo-mb-textarea"></textarea>' +
5000                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5001                 });
5002                 msgEl = bodyEl.dom.firstChild;
5003                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5004                 textboxEl.enableDisplayMode();
5005                 textboxEl.addKeyListener([10,13], function(){
5006                     if(dlg.isVisible() && opt && opt.buttons){
5007                         if(opt.buttons.ok){
5008                             handleButton("ok");
5009                         }else if(opt.buttons.yes){
5010                             handleButton("yes");
5011                         }
5012                     }
5013                 });
5014                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5015                 textareaEl.enableDisplayMode();
5016                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5017                 progressEl.enableDisplayMode();
5018                 
5019                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5020                 var pf = progressEl.dom.firstChild;
5021                 if (pf) {
5022                     pp = Roo.get(pf.firstChild);
5023                     pp.setHeight(pf.offsetHeight);
5024                 }
5025                 
5026             }
5027             return dlg;
5028         },
5029
5030         /**
5031          * Updates the message box body text
5032          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5033          * the XHTML-compliant non-breaking space character '&amp;#160;')
5034          * @return {Roo.MessageBox} This message box
5035          */
5036         updateText : function(text)
5037         {
5038             if(!dlg.isVisible() && !opt.width){
5039                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5040                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5041             }
5042             msgEl.innerHTML = text || '&#160;';
5043       
5044             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5045             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5046             var w = Math.max(
5047                     Math.min(opt.width || cw , this.maxWidth), 
5048                     Math.max(opt.minWidth || this.minWidth, bwidth)
5049             );
5050             if(opt.prompt){
5051                 activeTextEl.setWidth(w);
5052             }
5053             if(dlg.isVisible()){
5054                 dlg.fixedcenter = false;
5055             }
5056             // to big, make it scroll. = But as usual stupid IE does not support
5057             // !important..
5058             
5059             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5060                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5061                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5062             } else {
5063                 bodyEl.dom.style.height = '';
5064                 bodyEl.dom.style.overflowY = '';
5065             }
5066             if (cw > w) {
5067                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5068             } else {
5069                 bodyEl.dom.style.overflowX = '';
5070             }
5071             
5072             dlg.setContentSize(w, bodyEl.getHeight());
5073             if(dlg.isVisible()){
5074                 dlg.fixedcenter = true;
5075             }
5076             return this;
5077         },
5078
5079         /**
5080          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5081          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5082          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5083          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5084          * @return {Roo.MessageBox} This message box
5085          */
5086         updateProgress : function(value, text){
5087             if(text){
5088                 this.updateText(text);
5089             }
5090             
5091             if (pp) { // weird bug on my firefox - for some reason this is not defined
5092                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5093                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5094             }
5095             return this;
5096         },        
5097
5098         /**
5099          * Returns true if the message box is currently displayed
5100          * @return {Boolean} True if the message box is visible, else false
5101          */
5102         isVisible : function(){
5103             return dlg && dlg.isVisible();  
5104         },
5105
5106         /**
5107          * Hides the message box if it is displayed
5108          */
5109         hide : function(){
5110             if(this.isVisible()){
5111                 dlg.hide();
5112             }  
5113         },
5114
5115         /**
5116          * Displays a new message box, or reinitializes an existing message box, based on the config options
5117          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5118          * The following config object properties are supported:
5119          * <pre>
5120 Property    Type             Description
5121 ----------  ---------------  ------------------------------------------------------------------------------------
5122 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5123                                    closes (defaults to undefined)
5124 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5125                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5126 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5127                                    progress and wait dialogs will ignore this property and always hide the
5128                                    close button as they can only be closed programmatically.
5129 cls               String           A custom CSS class to apply to the message box element
5130 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5131                                    displayed (defaults to 75)
5132 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5133                                    function will be btn (the name of the button that was clicked, if applicable,
5134                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5135                                    Progress and wait dialogs will ignore this option since they do not respond to
5136                                    user actions and can only be closed programmatically, so any required function
5137                                    should be called by the same code after it closes the dialog.
5138 icon              String           A CSS class that provides a background image to be used as an icon for
5139                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5140 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5141 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5142 modal             Boolean          False to allow user interaction with the page while the message box is
5143                                    displayed (defaults to true)
5144 msg               String           A string that will replace the existing message box body text (defaults
5145                                    to the XHTML-compliant non-breaking space character '&#160;')
5146 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5147 progress          Boolean          True to display a progress bar (defaults to false)
5148 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5149 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5150 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5151 title             String           The title text
5152 value             String           The string value to set into the active textbox element if displayed
5153 wait              Boolean          True to display a progress bar (defaults to false)
5154 width             Number           The width of the dialog in pixels
5155 </pre>
5156          *
5157          * Example usage:
5158          * <pre><code>
5159 Roo.Msg.show({
5160    title: 'Address',
5161    msg: 'Please enter your address:',
5162    width: 300,
5163    buttons: Roo.MessageBox.OKCANCEL,
5164    multiline: true,
5165    fn: saveAddress,
5166    animEl: 'addAddressBtn'
5167 });
5168 </code></pre>
5169          * @param {Object} config Configuration options
5170          * @return {Roo.MessageBox} This message box
5171          */
5172         show : function(options)
5173         {
5174             
5175             // this causes nightmares if you show one dialog after another
5176             // especially on callbacks..
5177              
5178             if(this.isVisible()){
5179                 
5180                 this.hide();
5181                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5182                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5183                 Roo.log("New Dialog Message:" +  options.msg )
5184                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5185                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5186                 
5187             }
5188             var d = this.getDialog();
5189             opt = options;
5190             d.setTitle(opt.title || "&#160;");
5191             d.closeEl.setDisplayed(opt.closable !== false);
5192             activeTextEl = textboxEl;
5193             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5194             if(opt.prompt){
5195                 if(opt.multiline){
5196                     textboxEl.hide();
5197                     textareaEl.show();
5198                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5199                         opt.multiline : this.defaultTextHeight);
5200                     activeTextEl = textareaEl;
5201                 }else{
5202                     textboxEl.show();
5203                     textareaEl.hide();
5204                 }
5205             }else{
5206                 textboxEl.hide();
5207                 textareaEl.hide();
5208             }
5209             progressEl.setDisplayed(opt.progress === true);
5210             if (opt.progress) {
5211                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5212             }
5213             this.updateProgress(0);
5214             activeTextEl.dom.value = opt.value || "";
5215             if(opt.prompt){
5216                 dlg.setDefaultButton(activeTextEl);
5217             }else{
5218                 var bs = opt.buttons;
5219                 var db = null;
5220                 if(bs && bs.ok){
5221                     db = buttons["ok"];
5222                 }else if(bs && bs.yes){
5223                     db = buttons["yes"];
5224                 }
5225                 dlg.setDefaultButton(db);
5226             }
5227             bwidth = updateButtons(opt.buttons);
5228             this.updateText(opt.msg);
5229             if(opt.cls){
5230                 d.el.addClass(opt.cls);
5231             }
5232             d.proxyDrag = opt.proxyDrag === true;
5233             d.modal = opt.modal !== false;
5234             d.mask = opt.modal !== false ? mask : false;
5235             if(!d.isVisible()){
5236                 // force it to the end of the z-index stack so it gets a cursor in FF
5237                 document.body.appendChild(dlg.el.dom);
5238                 d.animateTarget = null;
5239                 d.show(options.animEl);
5240             }
5241             return this;
5242         },
5243
5244         /**
5245          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5246          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5247          * and closing the message box when the process is complete.
5248          * @param {String} title The title bar text
5249          * @param {String} msg The message box body text
5250          * @return {Roo.MessageBox} This message box
5251          */
5252         progress : function(title, msg){
5253             this.show({
5254                 title : title,
5255                 msg : msg,
5256                 buttons: false,
5257                 progress:true,
5258                 closable:false,
5259                 minWidth: this.minProgressWidth,
5260                 modal : true
5261             });
5262             return this;
5263         },
5264
5265         /**
5266          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5267          * If a callback function is passed it will be called after the user clicks the button, and the
5268          * id of the button that was clicked will be passed as the only parameter to the callback
5269          * (could also be the top-right close button).
5270          * @param {String} title The title bar text
5271          * @param {String} msg The message box body text
5272          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5273          * @param {Object} scope (optional) The scope of the callback function
5274          * @return {Roo.MessageBox} This message box
5275          */
5276         alert : function(title, msg, fn, scope)
5277         {
5278             this.show({
5279                 title : title,
5280                 msg : msg,
5281                 buttons: this.OK,
5282                 fn: fn,
5283                 closable : false,
5284                 scope : scope,
5285                 modal : true
5286             });
5287             return this;
5288         },
5289
5290         /**
5291          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5292          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5293          * You are responsible for closing the message box when the process is complete.
5294          * @param {String} msg The message box body text
5295          * @param {String} title (optional) The title bar text
5296          * @return {Roo.MessageBox} This message box
5297          */
5298         wait : function(msg, title){
5299             this.show({
5300                 title : title,
5301                 msg : msg,
5302                 buttons: false,
5303                 closable:false,
5304                 progress:true,
5305                 modal:true,
5306                 width:300,
5307                 wait:true
5308             });
5309             waitTimer = Roo.TaskMgr.start({
5310                 run: function(i){
5311                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5312                 },
5313                 interval: 1000
5314             });
5315             return this;
5316         },
5317
5318         /**
5319          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5320          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5321          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5322          * @param {String} title The title bar text
5323          * @param {String} msg The message box body text
5324          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5325          * @param {Object} scope (optional) The scope of the callback function
5326          * @return {Roo.MessageBox} This message box
5327          */
5328         confirm : function(title, msg, fn, scope){
5329             this.show({
5330                 title : title,
5331                 msg : msg,
5332                 buttons: this.YESNO,
5333                 fn: fn,
5334                 scope : scope,
5335                 modal : true
5336             });
5337             return this;
5338         },
5339
5340         /**
5341          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5342          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5343          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5344          * (could also be the top-right close button) and the text that was entered will be passed as the two
5345          * parameters to the callback.
5346          * @param {String} title The title bar text
5347          * @param {String} msg The message box body text
5348          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5349          * @param {Object} scope (optional) The scope of the callback function
5350          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5351          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         prompt : function(title, msg, fn, scope, multiline){
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OKCANCEL,
5359                 fn: fn,
5360                 minWidth:250,
5361                 scope : scope,
5362                 prompt:true,
5363                 multiline: multiline,
5364                 modal : true
5365             });
5366             return this;
5367         },
5368
5369         /**
5370          * Button config that displays a single OK button
5371          * @type Object
5372          */
5373         OK : {ok:true},
5374         /**
5375          * Button config that displays Yes and No buttons
5376          * @type Object
5377          */
5378         YESNO : {yes:true, no:true},
5379         /**
5380          * Button config that displays OK and Cancel buttons
5381          * @type Object
5382          */
5383         OKCANCEL : {ok:true, cancel:true},
5384         /**
5385          * Button config that displays Yes, No and Cancel buttons
5386          * @type Object
5387          */
5388         YESNOCANCEL : {yes:true, no:true, cancel:true},
5389
5390         /**
5391          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5392          * @type Number
5393          */
5394         defaultTextHeight : 75,
5395         /**
5396          * The maximum width in pixels of the message box (defaults to 600)
5397          * @type Number
5398          */
5399         maxWidth : 600,
5400         /**
5401          * The minimum width in pixels of the message box (defaults to 100)
5402          * @type Number
5403          */
5404         minWidth : 100,
5405         /**
5406          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5407          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5408          * @type Number
5409          */
5410         minProgressWidth : 250,
5411         /**
5412          * An object containing the default button text strings that can be overriden for localized language support.
5413          * Supported properties are: ok, cancel, yes and no.
5414          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5415          * @type Object
5416          */
5417         buttonText : {
5418             ok : "OK",
5419             cancel : "Cancel",
5420             yes : "Yes",
5421             no : "No"
5422         }
5423     };
5424 }();
5425
5426 /**
5427  * Shorthand for {@link Roo.MessageBox}
5428  */
5429 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5430 Roo.Msg = Roo.Msg || Roo.MessageBox;
5431 /*
5432  * - LGPL
5433  *
5434  * navbar
5435  * 
5436  */
5437
5438 /**
5439  * @class Roo.bootstrap.Navbar
5440  * @extends Roo.bootstrap.Component
5441  * Bootstrap Navbar class
5442
5443  * @constructor
5444  * Create a new Navbar
5445  * @param {Object} config The config object
5446  */
5447
5448
5449 Roo.bootstrap.Navbar = function(config){
5450     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5451     this.addEvents({
5452         // raw events
5453         /**
5454          * @event beforetoggle
5455          * Fire before toggle the menu
5456          * @param {Roo.EventObject} e
5457          */
5458         "beforetoggle" : true
5459     });
5460 };
5461
5462 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5463     
5464     
5465    
5466     // private
5467     navItems : false,
5468     loadMask : false,
5469     
5470     
5471     getAutoCreate : function(){
5472         
5473         
5474         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5475         
5476     },
5477     
5478     initEvents :function ()
5479     {
5480         //Roo.log(this.el.select('.navbar-toggle',true));
5481         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5482         
5483         var mark = {
5484             tag: "div",
5485             cls:"x-dlg-mask"
5486         };
5487         
5488         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5489         
5490         var size = this.el.getSize();
5491         this.maskEl.setSize(size.width, size.height);
5492         this.maskEl.enableDisplayMode("block");
5493         this.maskEl.hide();
5494         
5495         if(this.loadMask){
5496             this.maskEl.show();
5497         }
5498     },
5499     
5500     
5501     getChildContainer : function()
5502     {
5503         if (this.el && this.el.select('.collapse').getCount()) {
5504             return this.el.select('.collapse',true).first();
5505         }
5506         
5507         return this.el;
5508     },
5509     
5510     mask : function()
5511     {
5512         this.maskEl.show();
5513     },
5514     
5515     unmask : function()
5516     {
5517         this.maskEl.hide();
5518     },
5519     onToggle : function()
5520     {
5521         
5522         if(this.fireEvent('beforetoggle', this) === false){
5523             return;
5524         }
5525         var ce = this.el.select('.navbar-collapse',true).first();
5526       
5527         if (!ce.hasClass('show')) {
5528            this.expand();
5529         } else {
5530             this.collapse();
5531         }
5532         
5533         
5534     
5535     },
5536     /**
5537      * Expand the navbar pulldown 
5538      */
5539     expand : function ()
5540     {
5541        
5542         var ce = this.el.select('.navbar-collapse',true).first();
5543         if (ce.hasClass('collapsing')) {
5544             return;
5545         }
5546         ce.dom.style.height = '';
5547                // show it...
5548         ce.addClass('in'); // old...
5549         ce.removeClass('collapse');
5550         ce.addClass('show');
5551         var h = ce.getHeight();
5552         Roo.log(h);
5553         ce.removeClass('show');
5554         // at this point we should be able to see it..
5555         ce.addClass('collapsing');
5556         
5557         ce.setHeight(0); // resize it ...
5558         ce.on('transitionend', function() {
5559             //Roo.log('done transition');
5560             ce.removeClass('collapsing');
5561             ce.addClass('show');
5562             ce.removeClass('collapse');
5563
5564             ce.dom.style.height = '';
5565         }, this, { single: true} );
5566         ce.setHeight(h);
5567         ce.dom.scrollTop = 0;
5568     },
5569     /**
5570      * Collapse the navbar pulldown 
5571      */
5572     collapse : function()
5573     {
5574          var ce = this.el.select('.navbar-collapse',true).first();
5575        
5576         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5577             // it's collapsed or collapsing..
5578             return;
5579         }
5580         ce.removeClass('in'); // old...
5581         ce.setHeight(ce.getHeight());
5582         ce.removeClass('show');
5583         ce.addClass('collapsing');
5584         
5585         ce.on('transitionend', function() {
5586             ce.dom.style.height = '';
5587             ce.removeClass('collapsing');
5588             ce.addClass('collapse');
5589         }, this, { single: true} );
5590         ce.setHeight(0);
5591     }
5592     
5593     
5594     
5595 });
5596
5597
5598
5599  
5600
5601  /*
5602  * - LGPL
5603  *
5604  * navbar
5605  * 
5606  */
5607
5608 /**
5609  * @class Roo.bootstrap.NavSimplebar
5610  * @extends Roo.bootstrap.Navbar
5611  * Bootstrap Sidebar class
5612  *
5613  * @cfg {Boolean} inverse is inverted color
5614  * 
5615  * @cfg {String} type (nav | pills | tabs)
5616  * @cfg {Boolean} arrangement stacked | justified
5617  * @cfg {String} align (left | right) alignment
5618  * 
5619  * @cfg {Boolean} main (true|false) main nav bar? default false
5620  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5621  * 
5622  * @cfg {String} tag (header|footer|nav|div) default is nav 
5623
5624  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5625  * 
5626  * 
5627  * @constructor
5628  * Create a new Sidebar
5629  * @param {Object} config The config object
5630  */
5631
5632
5633 Roo.bootstrap.NavSimplebar = function(config){
5634     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5635 };
5636
5637 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5638     
5639     inverse: false,
5640     
5641     type: false,
5642     arrangement: '',
5643     align : false,
5644     
5645     weight : 'light',
5646     
5647     main : false,
5648     
5649     
5650     tag : false,
5651     
5652     
5653     getAutoCreate : function(){
5654         
5655         
5656         var cfg = {
5657             tag : this.tag || 'div',
5658             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5659         };
5660         if (['light','white'].indexOf(this.weight) > -1) {
5661             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5662         }
5663         cfg.cls += ' bg-' + this.weight;
5664         
5665         if (this.inverse) {
5666             cfg.cls += ' navbar-inverse';
5667             
5668         }
5669         
5670         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5671         
5672         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5673             return cfg;
5674         }
5675         
5676         
5677     
5678         
5679         cfg.cn = [
5680             {
5681                 cls: 'nav nav-' + this.xtype,
5682                 tag : 'ul'
5683             }
5684         ];
5685         
5686          
5687         this.type = this.type || 'nav';
5688         if (['tabs','pills'].indexOf(this.type) != -1) {
5689             cfg.cn[0].cls += ' nav-' + this.type
5690         
5691         
5692         } else {
5693             if (this.type!=='nav') {
5694                 Roo.log('nav type must be nav/tabs/pills')
5695             }
5696             cfg.cn[0].cls += ' navbar-nav'
5697         }
5698         
5699         
5700         
5701         
5702         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5703             cfg.cn[0].cls += ' nav-' + this.arrangement;
5704         }
5705         
5706         
5707         if (this.align === 'right') {
5708             cfg.cn[0].cls += ' navbar-right';
5709         }
5710         
5711         
5712         
5713         
5714         return cfg;
5715     
5716         
5717     }
5718     
5719     
5720     
5721 });
5722
5723
5724
5725  
5726
5727  
5728        /*
5729  * - LGPL
5730  *
5731  * navbar
5732  * navbar-fixed-top
5733  * navbar-expand-md  fixed-top 
5734  */
5735
5736 /**
5737  * @class Roo.bootstrap.NavHeaderbar
5738  * @extends Roo.bootstrap.NavSimplebar
5739  * Bootstrap Sidebar class
5740  *
5741  * @cfg {String} brand what is brand
5742  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5743  * @cfg {String} brand_href href of the brand
5744  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5745  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5746  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5747  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5748  * 
5749  * @constructor
5750  * Create a new Sidebar
5751  * @param {Object} config The config object
5752  */
5753
5754
5755 Roo.bootstrap.NavHeaderbar = function(config){
5756     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5757       
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5761     
5762     position: '',
5763     brand: '',
5764     brand_href: false,
5765     srButton : true,
5766     autohide : false,
5767     desktopCenter : false,
5768    
5769     
5770     getAutoCreate : function(){
5771         
5772         var   cfg = {
5773             tag: this.nav || 'nav',
5774             cls: 'navbar navbar-expand-md',
5775             role: 'navigation',
5776             cn: []
5777         };
5778         
5779         var cn = cfg.cn;
5780         if (this.desktopCenter) {
5781             cn.push({cls : 'container', cn : []});
5782             cn = cn[0].cn;
5783         }
5784         
5785         if(this.srButton){
5786             var btn = {
5787                 tag: 'button',
5788                 type: 'button',
5789                 cls: 'navbar-toggle navbar-toggler',
5790                 'data-toggle': 'collapse',
5791                 cn: [
5792                     {
5793                         tag: 'span',
5794                         cls: 'sr-only',
5795                         html: 'Toggle navigation'
5796                     },
5797                     {
5798                         tag: 'span',
5799                         cls: 'icon-bar navbar-toggler-icon'
5800                     },
5801                     {
5802                         tag: 'span',
5803                         cls: 'icon-bar'
5804                     },
5805                     {
5806                         tag: 'span',
5807                         cls: 'icon-bar'
5808                     }
5809                 ]
5810             };
5811             
5812             cn.push( Roo.bootstrap.version == 4 ? btn : {
5813                 tag: 'div',
5814                 cls: 'navbar-header',
5815                 cn: [
5816                     btn
5817                 ]
5818             });
5819         }
5820         
5821         cn.push({
5822             tag: 'div',
5823             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5824             cn : []
5825         });
5826         
5827         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5828         
5829         if (['light','white'].indexOf(this.weight) > -1) {
5830             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5831         }
5832         cfg.cls += ' bg-' + this.weight;
5833         
5834         
5835         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5836             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5837             
5838             // tag can override this..
5839             
5840             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5841         }
5842         
5843         if (this.brand !== '') {
5844             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5845             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5846                 tag: 'a',
5847                 href: this.brand_href ? this.brand_href : '#',
5848                 cls: 'navbar-brand',
5849                 cn: [
5850                 this.brand
5851                 ]
5852             });
5853         }
5854         
5855         if(this.main){
5856             cfg.cls += ' main-nav';
5857         }
5858         
5859         
5860         return cfg;
5861
5862         
5863     },
5864     getHeaderChildContainer : function()
5865     {
5866         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5867             return this.el.select('.navbar-header',true).first();
5868         }
5869         
5870         return this.getChildContainer();
5871     },
5872     
5873     getChildContainer : function()
5874     {
5875          
5876         return this.el.select('.roo-navbar-collapse',true).first();
5877          
5878         
5879     },
5880     
5881     initEvents : function()
5882     {
5883         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5884         
5885         if (this.autohide) {
5886             
5887             var prevScroll = 0;
5888             var ft = this.el;
5889             
5890             Roo.get(document).on('scroll',function(e) {
5891                 var ns = Roo.get(document).getScroll().top;
5892                 var os = prevScroll;
5893                 prevScroll = ns;
5894                 
5895                 if(ns > os){
5896                     ft.removeClass('slideDown');
5897                     ft.addClass('slideUp');
5898                     return;
5899                 }
5900                 ft.removeClass('slideUp');
5901                 ft.addClass('slideDown');
5902                  
5903               
5904           },this);
5905         }
5906     }    
5907     
5908 });
5909
5910
5911
5912  
5913
5914  /*
5915  * - LGPL
5916  *
5917  * navbar
5918  * 
5919  */
5920
5921 /**
5922  * @class Roo.bootstrap.NavSidebar
5923  * @extends Roo.bootstrap.Navbar
5924  * Bootstrap Sidebar class
5925  * 
5926  * @constructor
5927  * Create a new Sidebar
5928  * @param {Object} config The config object
5929  */
5930
5931
5932 Roo.bootstrap.NavSidebar = function(config){
5933     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5934 };
5935
5936 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5937     
5938     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5939     
5940     getAutoCreate : function(){
5941         
5942         
5943         return  {
5944             tag: 'div',
5945             cls: 'sidebar sidebar-nav'
5946         };
5947     
5948         
5949     }
5950     
5951     
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * nav group
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavGroup
5968  * @extends Roo.bootstrap.Component
5969  * Bootstrap NavGroup class
5970  * @cfg {String} align (left|right)
5971  * @cfg {Boolean} inverse
5972  * @cfg {String} type (nav|pills|tab) default nav
5973  * @cfg {String} navId - reference Id for navbar.
5974  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5975  * 
5976  * @constructor
5977  * Create a new nav group
5978  * @param {Object} config The config object
5979  */
5980
5981 Roo.bootstrap.NavGroup = function(config){
5982     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5983     this.navItems = [];
5984    
5985     Roo.bootstrap.NavGroup.register(this);
5986      this.addEvents({
5987         /**
5988              * @event changed
5989              * Fires when the active item changes
5990              * @param {Roo.bootstrap.NavGroup} this
5991              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5992              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5993          */
5994         'changed': true
5995      });
5996     
5997 };
5998
5999 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6000     
6001     align: '',
6002     inverse: false,
6003     form: false,
6004     type: 'nav',
6005     navId : '',
6006     // private
6007     pilltype : true,
6008     
6009     navItems : false, 
6010     
6011     getAutoCreate : function()
6012     {
6013         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6014         
6015         cfg = {
6016             tag : 'ul',
6017             cls: 'nav' 
6018         };
6019         if (Roo.bootstrap.version == 4) {
6020             if (['tabs','pills'].indexOf(this.type) != -1) {
6021                 cfg.cls += ' nav-' + this.type; 
6022             } else {
6023                 // trying to remove so header bar can right align top?
6024                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6025                     // do not use on header bar... 
6026                     cfg.cls += ' navbar-nav';
6027                 }
6028             }
6029             
6030         } else {
6031             if (['tabs','pills'].indexOf(this.type) != -1) {
6032                 cfg.cls += ' nav-' + this.type
6033             } else {
6034                 if (this.type !== 'nav') {
6035                     Roo.log('nav type must be nav/tabs/pills')
6036                 }
6037                 cfg.cls += ' navbar-nav'
6038             }
6039         }
6040         
6041         if (this.parent() && this.parent().sidebar) {
6042             cfg = {
6043                 tag: 'ul',
6044                 cls: 'dashboard-menu sidebar-menu'
6045             };
6046             
6047             return cfg;
6048         }
6049         
6050         if (this.form === true) {
6051             cfg = {
6052                 tag: 'form',
6053                 cls: 'navbar-form form-inline'
6054             };
6055             //nav navbar-right ml-md-auto
6056             if (this.align === 'right') {
6057                 cfg.cls += ' navbar-right ml-md-auto';
6058             } else {
6059                 cfg.cls += ' navbar-left';
6060             }
6061         }
6062         
6063         if (this.align === 'right') {
6064             cfg.cls += ' navbar-right ml-md-auto';
6065         } else {
6066             cfg.cls += ' mr-auto';
6067         }
6068         
6069         if (this.inverse) {
6070             cfg.cls += ' navbar-inverse';
6071             
6072         }
6073         
6074         
6075         return cfg;
6076     },
6077     /**
6078     * sets the active Navigation item
6079     * @param {Roo.bootstrap.NavItem} the new current navitem
6080     */
6081     setActiveItem : function(item)
6082     {
6083         var prev = false;
6084         Roo.each(this.navItems, function(v){
6085             if (v == item) {
6086                 return ;
6087             }
6088             if (v.isActive()) {
6089                 v.setActive(false, true);
6090                 prev = v;
6091                 
6092             }
6093             
6094         });
6095
6096         item.setActive(true, true);
6097         this.fireEvent('changed', this, item, prev);
6098         
6099         
6100     },
6101     /**
6102     * gets the active Navigation item
6103     * @return {Roo.bootstrap.NavItem} the current navitem
6104     */
6105     getActive : function()
6106     {
6107         
6108         var prev = false;
6109         Roo.each(this.navItems, function(v){
6110             
6111             if (v.isActive()) {
6112                 prev = v;
6113                 
6114             }
6115             
6116         });
6117         return prev;
6118     },
6119     
6120     indexOfNav : function()
6121     {
6122         
6123         var prev = false;
6124         Roo.each(this.navItems, function(v,i){
6125             
6126             if (v.isActive()) {
6127                 prev = i;
6128                 
6129             }
6130             
6131         });
6132         return prev;
6133     },
6134     /**
6135     * adds a Navigation item
6136     * @param {Roo.bootstrap.NavItem} the navitem to add
6137     */
6138     addItem : function(cfg)
6139     {
6140         if (this.form && Roo.bootstrap.version == 4) {
6141             cfg.tag = 'div';
6142         }
6143         var cn = new Roo.bootstrap.NavItem(cfg);
6144         this.register(cn);
6145         cn.parentId = this.id;
6146         cn.onRender(this.el, null);
6147         return cn;
6148     },
6149     /**
6150     * register a Navigation item
6151     * @param {Roo.bootstrap.NavItem} the navitem to add
6152     */
6153     register : function(item)
6154     {
6155         this.navItems.push( item);
6156         item.navId = this.navId;
6157     
6158     },
6159     
6160     /**
6161     * clear all the Navigation item
6162     */
6163    
6164     clearAll : function()
6165     {
6166         this.navItems = [];
6167         this.el.dom.innerHTML = '';
6168     },
6169     
6170     getNavItem: function(tabId)
6171     {
6172         var ret = false;
6173         Roo.each(this.navItems, function(e) {
6174             if (e.tabId == tabId) {
6175                ret =  e;
6176                return false;
6177             }
6178             return true;
6179             
6180         });
6181         return ret;
6182     },
6183     
6184     setActiveNext : function()
6185     {
6186         var i = this.indexOfNav(this.getActive());
6187         if (i > this.navItems.length) {
6188             return;
6189         }
6190         this.setActiveItem(this.navItems[i+1]);
6191     },
6192     setActivePrev : function()
6193     {
6194         var i = this.indexOfNav(this.getActive());
6195         if (i  < 1) {
6196             return;
6197         }
6198         this.setActiveItem(this.navItems[i-1]);
6199     },
6200     clearWasActive : function(except) {
6201         Roo.each(this.navItems, function(e) {
6202             if (e.tabId != except.tabId && e.was_active) {
6203                e.was_active = false;
6204                return false;
6205             }
6206             return true;
6207             
6208         });
6209     },
6210     getWasActive : function ()
6211     {
6212         var r = false;
6213         Roo.each(this.navItems, function(e) {
6214             if (e.was_active) {
6215                r = e;
6216                return false;
6217             }
6218             return true;
6219             
6220         });
6221         return r;
6222     }
6223     
6224     
6225 });
6226
6227  
6228 Roo.apply(Roo.bootstrap.NavGroup, {
6229     
6230     groups: {},
6231      /**
6232     * register a Navigation Group
6233     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6234     */
6235     register : function(navgrp)
6236     {
6237         this.groups[navgrp.navId] = navgrp;
6238         
6239     },
6240     /**
6241     * fetch a Navigation Group based on the navigation ID
6242     * @param {string} the navgroup to add
6243     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6244     */
6245     get: function(navId) {
6246         if (typeof(this.groups[navId]) == 'undefined') {
6247             return false;
6248             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6249         }
6250         return this.groups[navId] ;
6251     }
6252     
6253     
6254     
6255 });
6256
6257  /*
6258  * - LGPL
6259  *
6260  * row
6261  * 
6262  */
6263
6264 /**
6265  * @class Roo.bootstrap.NavItem
6266  * @extends Roo.bootstrap.Component
6267  * Bootstrap Navbar.NavItem class
6268  * @cfg {String} href  link to
6269  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6270  * @cfg {Boolean} button_outline show and outlined button
6271  * @cfg {String} html content of button
6272  * @cfg {String} badge text inside badge
6273  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6274  * @cfg {String} glyphicon DEPRICATED - use fa
6275  * @cfg {String} icon DEPRICATED - use fa
6276  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6277  * @cfg {Boolean} active Is item active
6278  * @cfg {Boolean} disabled Is item disabled
6279  * @cfg {String} linkcls  Link Class
6280  * @cfg {Boolean} preventDefault (true | false) default false
6281  * @cfg {String} tabId the tab that this item activates.
6282  * @cfg {String} tagtype (a|span) render as a href or span?
6283  * @cfg {Boolean} animateRef (true|false) link to element default false  
6284   
6285  * @constructor
6286  * Create a new Navbar Item
6287  * @param {Object} config The config object
6288  */
6289 Roo.bootstrap.NavItem = function(config){
6290     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6291     this.addEvents({
6292         // raw events
6293         /**
6294          * @event click
6295          * The raw click event for the entire grid.
6296          * @param {Roo.EventObject} e
6297          */
6298         "click" : true,
6299          /**
6300             * @event changed
6301             * Fires when the active item active state changes
6302             * @param {Roo.bootstrap.NavItem} this
6303             * @param {boolean} state the new state
6304              
6305          */
6306         'changed': true,
6307         /**
6308             * @event scrollto
6309             * Fires when scroll to element
6310             * @param {Roo.bootstrap.NavItem} this
6311             * @param {Object} options
6312             * @param {Roo.EventObject} e
6313              
6314          */
6315         'scrollto': true
6316     });
6317    
6318 };
6319
6320 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6321     
6322     href: false,
6323     html: '',
6324     badge: '',
6325     icon: false,
6326     fa : false,
6327     glyphicon: false,
6328     active: false,
6329     preventDefault : false,
6330     tabId : false,
6331     tagtype : 'a',
6332     tag: 'li',
6333     disabled : false,
6334     animateRef : false,
6335     was_active : false,
6336     button_weight : '',
6337     button_outline : false,
6338     linkcls : '',
6339     navLink: false,
6340     
6341     getAutoCreate : function(){
6342          
6343         var cfg = {
6344             tag: this.tag,
6345             cls: 'nav-item'
6346         };
6347         
6348         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6349         
6350         if (this.active) {
6351             cfg.cls +=  ' active' ;
6352         }
6353         if (this.disabled) {
6354             cfg.cls += ' disabled';
6355         }
6356         
6357         // BS4 only?
6358         if (this.button_weight.length) {
6359             cfg.tag = this.href ? 'a' : 'button';
6360             cfg.html = this.html || '';
6361             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6362             if (this.href) {
6363                 cfg.href = this.href;
6364             }
6365             if (this.fa) {
6366                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6367             } else {
6368                 cfg.cls += " nav-html";
6369             }
6370             
6371             // menu .. should add dropdown-menu class - so no need for carat..
6372             
6373             if (this.badge !== '') {
6374                  
6375                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6376             }
6377             return cfg;
6378         }
6379         
6380         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6381             cfg.cn = [
6382                 {
6383                     tag: this.tagtype,
6384                     href : this.href || "#",
6385                     html: this.html || '',
6386                     cls : ''
6387                 }
6388             ];
6389             if (this.tagtype == 'a') {
6390                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6391         
6392             }
6393             if (this.icon) {
6394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6395             } else  if (this.fa) {
6396                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6397             } else if(this.glyphicon) {
6398                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6399             } else {
6400                 cfg.cn[0].cls += " nav-html";
6401             }
6402             
6403             if (this.menu) {
6404                 cfg.cn[0].html += " <span class='caret'></span>";
6405              
6406             }
6407             
6408             if (this.badge !== '') {
6409                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6410             }
6411         }
6412         
6413         
6414         
6415         return cfg;
6416     },
6417     onRender : function(ct, position)
6418     {
6419        // Roo.log("Call onRender: " + this.xtype);
6420         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6421             this.tag = 'div';
6422         }
6423         
6424         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6425         this.navLink = this.el.select('.nav-link',true).first();
6426         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6427         return ret;
6428     },
6429       
6430     
6431     initEvents: function() 
6432     {
6433         if (typeof (this.menu) != 'undefined') {
6434             this.menu.parentType = this.xtype;
6435             this.menu.triggerEl = this.el;
6436             this.menu = this.addxtype(Roo.apply({}, this.menu));
6437         }
6438         
6439         this.el.on('click', this.onClick, this);
6440         
6441         //if(this.tagtype == 'span'){
6442         //    this.el.select('span',true).on('click', this.onClick, this);
6443         //}
6444        
6445         // at this point parent should be available..
6446         this.parent().register(this);
6447     },
6448     
6449     onClick : function(e)
6450     {
6451         if (e.getTarget('.dropdown-menu-item')) {
6452             // did you click on a menu itemm.... - then don't trigger onclick..
6453             return;
6454         }
6455         
6456         if(
6457                 this.preventDefault || 
6458                 this.href == '#' 
6459         ){
6460             Roo.log("NavItem - prevent Default?");
6461             e.preventDefault();
6462         }
6463         
6464         if (this.disabled) {
6465             return;
6466         }
6467         
6468         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6469         if (tg && tg.transition) {
6470             Roo.log("waiting for the transitionend");
6471             return;
6472         }
6473         
6474         
6475         
6476         //Roo.log("fire event clicked");
6477         if(this.fireEvent('click', this, e) === false){
6478             return;
6479         };
6480         
6481         if(this.tagtype == 'span'){
6482             return;
6483         }
6484         
6485         //Roo.log(this.href);
6486         var ael = this.el.select('a',true).first();
6487         //Roo.log(ael);
6488         
6489         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6490             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6491             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6492                 return; // ignore... - it's a 'hash' to another page.
6493             }
6494             Roo.log("NavItem - prevent Default?");
6495             e.preventDefault();
6496             this.scrollToElement(e);
6497         }
6498         
6499         
6500         var p =  this.parent();
6501    
6502         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6503             if (typeof(p.setActiveItem) !== 'undefined') {
6504                 p.setActiveItem(this);
6505             }
6506         }
6507         
6508         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6509         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6510             // remove the collapsed menu expand...
6511             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6512         }
6513     },
6514     
6515     isActive: function () {
6516         return this.active
6517     },
6518     setActive : function(state, fire, is_was_active)
6519     {
6520         if (this.active && !state && this.navId) {
6521             this.was_active = true;
6522             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6523             if (nv) {
6524                 nv.clearWasActive(this);
6525             }
6526             
6527         }
6528         this.active = state;
6529         
6530         if (!state ) {
6531             this.el.removeClass('active');
6532             this.navLink ? this.navLink.removeClass('active') : false;
6533         } else if (!this.el.hasClass('active')) {
6534             
6535             this.el.addClass('active');
6536             if (Roo.bootstrap.version == 4 && this.navLink ) {
6537                 this.navLink.addClass('active');
6538             }
6539             
6540         }
6541         if (fire) {
6542             this.fireEvent('changed', this, state);
6543         }
6544         
6545         // show a panel if it's registered and related..
6546         
6547         if (!this.navId || !this.tabId || !state || is_was_active) {
6548             return;
6549         }
6550         
6551         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6552         if (!tg) {
6553             return;
6554         }
6555         var pan = tg.getPanelByName(this.tabId);
6556         if (!pan) {
6557             return;
6558         }
6559         // if we can not flip to new panel - go back to old nav highlight..
6560         if (false == tg.showPanel(pan)) {
6561             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6562             if (nv) {
6563                 var onav = nv.getWasActive();
6564                 if (onav) {
6565                     onav.setActive(true, false, true);
6566                 }
6567             }
6568             
6569         }
6570         
6571         
6572         
6573     },
6574      // this should not be here...
6575     setDisabled : function(state)
6576     {
6577         this.disabled = state;
6578         if (!state ) {
6579             this.el.removeClass('disabled');
6580         } else if (!this.el.hasClass('disabled')) {
6581             this.el.addClass('disabled');
6582         }
6583         
6584     },
6585     
6586     /**
6587      * Fetch the element to display the tooltip on.
6588      * @return {Roo.Element} defaults to this.el
6589      */
6590     tooltipEl : function()
6591     {
6592         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6593     },
6594     
6595     scrollToElement : function(e)
6596     {
6597         var c = document.body;
6598         
6599         /*
6600          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6601          */
6602         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6603             c = document.documentElement;
6604         }
6605         
6606         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6607         
6608         if(!target){
6609             return;
6610         }
6611
6612         var o = target.calcOffsetsTo(c);
6613         
6614         var options = {
6615             target : target,
6616             value : o[1]
6617         };
6618         
6619         this.fireEvent('scrollto', this, options, e);
6620         
6621         Roo.get(c).scrollTo('top', options.value, true);
6622         
6623         return;
6624     },
6625     /**
6626      * Set the HTML (text content) of the item
6627      * @param {string} html  content for the nav item
6628      */
6629     setHtml : function(html)
6630     {
6631         this.html = html;
6632         this.htmlEl.dom.innerHTML = html;
6633         
6634     } 
6635 });
6636  
6637
6638  /*
6639  * - LGPL
6640  *
6641  * sidebar item
6642  *
6643  *  li
6644  *    <span> icon </span>
6645  *    <span> text </span>
6646  *    <span>badge </span>
6647  */
6648
6649 /**
6650  * @class Roo.bootstrap.NavSidebarItem
6651  * @extends Roo.bootstrap.NavItem
6652  * Bootstrap Navbar.NavSidebarItem class
6653  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6654  * {Boolean} open is the menu open
6655  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6656  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6657  * {String} buttonSize (sm|md|lg)the extra classes for the button
6658  * {Boolean} showArrow show arrow next to the text (default true)
6659  * @constructor
6660  * Create a new Navbar Button
6661  * @param {Object} config The config object
6662  */
6663 Roo.bootstrap.NavSidebarItem = function(config){
6664     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6665     this.addEvents({
6666         // raw events
6667         /**
6668          * @event click
6669          * The raw click event for the entire grid.
6670          * @param {Roo.EventObject} e
6671          */
6672         "click" : true,
6673          /**
6674             * @event changed
6675             * Fires when the active item active state changes
6676             * @param {Roo.bootstrap.NavSidebarItem} this
6677             * @param {boolean} state the new state
6678              
6679          */
6680         'changed': true
6681     });
6682    
6683 };
6684
6685 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6686     
6687     badgeWeight : 'default',
6688     
6689     open: false,
6690     
6691     buttonView : false,
6692     
6693     buttonWeight : 'default',
6694     
6695     buttonSize : 'md',
6696     
6697     showArrow : true,
6698     
6699     getAutoCreate : function(){
6700         
6701         
6702         var a = {
6703                 tag: 'a',
6704                 href : this.href || '#',
6705                 cls: '',
6706                 html : '',
6707                 cn : []
6708         };
6709         
6710         if(this.buttonView){
6711             a = {
6712                 tag: 'button',
6713                 href : this.href || '#',
6714                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6715                 html : this.html,
6716                 cn : []
6717             };
6718         }
6719         
6720         var cfg = {
6721             tag: 'li',
6722             cls: '',
6723             cn: [ a ]
6724         };
6725         
6726         if (this.active) {
6727             cfg.cls += ' active';
6728         }
6729         
6730         if (this.disabled) {
6731             cfg.cls += ' disabled';
6732         }
6733         if (this.open) {
6734             cfg.cls += ' open x-open';
6735         }
6736         // left icon..
6737         if (this.glyphicon || this.icon) {
6738             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6739             a.cn.push({ tag : 'i', cls : c }) ;
6740         }
6741         
6742         if(!this.buttonView){
6743             var span = {
6744                 tag: 'span',
6745                 html : this.html || ''
6746             };
6747
6748             a.cn.push(span);
6749             
6750         }
6751         
6752         if (this.badge !== '') {
6753             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6754         }
6755         
6756         if (this.menu) {
6757             
6758             if(this.showArrow){
6759                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6760             }
6761             
6762             a.cls += ' dropdown-toggle treeview' ;
6763         }
6764         
6765         return cfg;
6766     },
6767     
6768     initEvents : function()
6769     { 
6770         if (typeof (this.menu) != 'undefined') {
6771             this.menu.parentType = this.xtype;
6772             this.menu.triggerEl = this.el;
6773             this.menu = this.addxtype(Roo.apply({}, this.menu));
6774         }
6775         
6776         this.el.on('click', this.onClick, this);
6777         
6778         if(this.badge !== ''){
6779             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6780         }
6781         
6782     },
6783     
6784     onClick : function(e)
6785     {
6786         if(this.disabled){
6787             e.preventDefault();
6788             return;
6789         }
6790         
6791         if(this.preventDefault){
6792             e.preventDefault();
6793         }
6794         
6795         this.fireEvent('click', this, e);
6796     },
6797     
6798     disable : function()
6799     {
6800         this.setDisabled(true);
6801     },
6802     
6803     enable : function()
6804     {
6805         this.setDisabled(false);
6806     },
6807     
6808     setDisabled : function(state)
6809     {
6810         if(this.disabled == state){
6811             return;
6812         }
6813         
6814         this.disabled = state;
6815         
6816         if (state) {
6817             this.el.addClass('disabled');
6818             return;
6819         }
6820         
6821         this.el.removeClass('disabled');
6822         
6823         return;
6824     },
6825     
6826     setActive : function(state)
6827     {
6828         if(this.active == state){
6829             return;
6830         }
6831         
6832         this.active = state;
6833         
6834         if (state) {
6835             this.el.addClass('active');
6836             return;
6837         }
6838         
6839         this.el.removeClass('active');
6840         
6841         return;
6842     },
6843     
6844     isActive: function () 
6845     {
6846         return this.active;
6847     },
6848     
6849     setBadge : function(str)
6850     {
6851         if(!this.badgeEl){
6852             return;
6853         }
6854         
6855         this.badgeEl.dom.innerHTML = str;
6856     }
6857     
6858    
6859      
6860  
6861 });
6862  
6863
6864  /*
6865  * - LGPL
6866  *
6867  *  Breadcrumb Nav
6868  * 
6869  */
6870 Roo.namespace('Roo.bootstrap.breadcrumb');
6871
6872
6873 /**
6874  * @class Roo.bootstrap.breadcrumb.Nav
6875  * @extends Roo.bootstrap.Component
6876  * Bootstrap Breadcrumb Nav Class
6877  *  
6878  * @children Roo.bootstrap.breadcrumb.Item
6879  * 
6880  * @constructor
6881  * Create a new breadcrumb.Nav
6882  * @param {Object} config The config object
6883  */
6884
6885
6886 Roo.bootstrap.breadcrumb.Nav = function(config){
6887     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6888     
6889     
6890 };
6891
6892 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6893     
6894     getAutoCreate : function()
6895     {
6896
6897         var cfg = {
6898             tag: 'nav',
6899             cn : [
6900                 {
6901                     tag : 'ol',
6902                     cls : 'breadcrumb'
6903                 }
6904             ]
6905             
6906         };
6907           
6908         return cfg;
6909     },
6910     
6911     initEvents: function()
6912     {
6913         this.olEl = this.el.select('ol',true).first();    
6914     },
6915     getChildContainer : function()
6916     {
6917         return this.olEl;  
6918     }
6919     
6920 });
6921
6922  /*
6923  * - LGPL
6924  *
6925  *  Breadcrumb Item
6926  * 
6927  */
6928
6929
6930 /**
6931  * @class Roo.bootstrap.breadcrumb.Nav
6932  * @extends Roo.bootstrap.Component
6933  * Bootstrap Breadcrumb Nav Class
6934  *  
6935  * @children Roo.bootstrap.breadcrumb.Component
6936  * @cfg {String} html the content of the link.
6937  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6938  * @cfg {Boolean} active is it active
6939
6940  * 
6941  * @constructor
6942  * Create a new breadcrumb.Nav
6943  * @param {Object} config The config object
6944  */
6945
6946 Roo.bootstrap.breadcrumb.Item = function(config){
6947     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6948     this.addEvents({
6949         // img events
6950         /**
6951          * @event click
6952          * The img click event for the img.
6953          * @param {Roo.EventObject} e
6954          */
6955         "click" : true
6956     });
6957     
6958 };
6959
6960 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6961     
6962     href: false,
6963     html : '',
6964     
6965     getAutoCreate : function()
6966     {
6967
6968         var cfg = {
6969             tag: 'li',
6970             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6971         };
6972         if (this.href !== false) {
6973             cfg.cn = [{
6974                 tag : 'a',
6975                 href : this.href,
6976                 html : this.html
6977             }];
6978         } else {
6979             cfg.html = this.html;
6980         }
6981         
6982         return cfg;
6983     },
6984     
6985     initEvents: function()
6986     {
6987         if (this.href) {
6988             this.el.select('a', true).first().on('click',this.onClick, this)
6989         }
6990         
6991     },
6992     onClick : function(e)
6993     {
6994         e.preventDefault();
6995         this.fireEvent('click',this,  e);
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  * row
7004  * 
7005  */
7006
7007 /**
7008  * @class Roo.bootstrap.Row
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Row class (contains columns...)
7011  * 
7012  * @constructor
7013  * Create a new Row
7014  * @param {Object} config The config object
7015  */
7016
7017 Roo.bootstrap.Row = function(config){
7018     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7019 };
7020
7021 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7022     
7023     getAutoCreate : function(){
7024        return {
7025             cls: 'row clearfix'
7026        };
7027     }
7028     
7029     
7030 });
7031
7032  
7033
7034  /*
7035  * - LGPL
7036  *
7037  * pagination
7038  * 
7039  */
7040
7041 /**
7042  * @class Roo.bootstrap.Pagination
7043  * @extends Roo.bootstrap.Component
7044  * Bootstrap Pagination class
7045  * @cfg {String} size xs | sm | md | lg
7046  * @cfg {Boolean} inverse false | true
7047  * 
7048  * @constructor
7049  * Create a new Pagination
7050  * @param {Object} config The config object
7051  */
7052
7053 Roo.bootstrap.Pagination = function(config){
7054     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7055 };
7056
7057 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7058     
7059     cls: false,
7060     size: false,
7061     inverse: false,
7062     
7063     getAutoCreate : function(){
7064         var cfg = {
7065             tag: 'ul',
7066                 cls: 'pagination'
7067         };
7068         if (this.inverse) {
7069             cfg.cls += ' inverse';
7070         }
7071         if (this.html) {
7072             cfg.html=this.html;
7073         }
7074         if (this.cls) {
7075             cfg.cls += " " + this.cls;
7076         }
7077         return cfg;
7078     }
7079    
7080 });
7081
7082  
7083
7084  /*
7085  * - LGPL
7086  *
7087  * Pagination item
7088  * 
7089  */
7090
7091
7092 /**
7093  * @class Roo.bootstrap.PaginationItem
7094  * @extends Roo.bootstrap.Component
7095  * Bootstrap PaginationItem class
7096  * @cfg {String} html text
7097  * @cfg {String} href the link
7098  * @cfg {Boolean} preventDefault (true | false) default true
7099  * @cfg {Boolean} active (true | false) default false
7100  * @cfg {Boolean} disabled default false
7101  * 
7102  * 
7103  * @constructor
7104  * Create a new PaginationItem
7105  * @param {Object} config The config object
7106  */
7107
7108
7109 Roo.bootstrap.PaginationItem = function(config){
7110     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7111     this.addEvents({
7112         // raw events
7113         /**
7114          * @event click
7115          * The raw click event for the entire grid.
7116          * @param {Roo.EventObject} e
7117          */
7118         "click" : true
7119     });
7120 };
7121
7122 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7123     
7124     href : false,
7125     html : false,
7126     preventDefault: true,
7127     active : false,
7128     cls : false,
7129     disabled: false,
7130     
7131     getAutoCreate : function(){
7132         var cfg= {
7133             tag: 'li',
7134             cn: [
7135                 {
7136                     tag : 'a',
7137                     href : this.href ? this.href : '#',
7138                     html : this.html ? this.html : ''
7139                 }
7140             ]
7141         };
7142         
7143         if(this.cls){
7144             cfg.cls = this.cls;
7145         }
7146         
7147         if(this.disabled){
7148             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7149         }
7150         
7151         if(this.active){
7152             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7153         }
7154         
7155         return cfg;
7156     },
7157     
7158     initEvents: function() {
7159         
7160         this.el.on('click', this.onClick, this);
7161         
7162     },
7163     onClick : function(e)
7164     {
7165         Roo.log('PaginationItem on click ');
7166         if(this.preventDefault){
7167             e.preventDefault();
7168         }
7169         
7170         if(this.disabled){
7171             return;
7172         }
7173         
7174         this.fireEvent('click', this, e);
7175     }
7176    
7177 });
7178
7179  
7180
7181  /*
7182  * - LGPL
7183  *
7184  * slider
7185  * 
7186  */
7187
7188
7189 /**
7190  * @class Roo.bootstrap.Slider
7191  * @extends Roo.bootstrap.Component
7192  * Bootstrap Slider class
7193  *    
7194  * @constructor
7195  * Create a new Slider
7196  * @param {Object} config The config object
7197  */
7198
7199 Roo.bootstrap.Slider = function(config){
7200     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7201 };
7202
7203 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7204     
7205     getAutoCreate : function(){
7206         
7207         var cfg = {
7208             tag: 'div',
7209             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7210             cn: [
7211                 {
7212                     tag: 'a',
7213                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7214                 }
7215             ]
7216         };
7217         
7218         return cfg;
7219     }
7220    
7221 });
7222
7223  /*
7224  * Based on:
7225  * Ext JS Library 1.1.1
7226  * Copyright(c) 2006-2007, Ext JS, LLC.
7227  *
7228  * Originally Released Under LGPL - original licence link has changed is not relivant.
7229  *
7230  * Fork - LGPL
7231  * <script type="text/javascript">
7232  */
7233  
7234
7235 /**
7236  * @class Roo.grid.ColumnModel
7237  * @extends Roo.util.Observable
7238  * This is the default implementation of a ColumnModel used by the Grid. It defines
7239  * the columns in the grid.
7240  * <br>Usage:<br>
7241  <pre><code>
7242  var colModel = new Roo.grid.ColumnModel([
7243         {header: "Ticker", width: 60, sortable: true, locked: true},
7244         {header: "Company Name", width: 150, sortable: true},
7245         {header: "Market Cap.", width: 100, sortable: true},
7246         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7247         {header: "Employees", width: 100, sortable: true, resizable: false}
7248  ]);
7249  </code></pre>
7250  * <p>
7251  
7252  * The config options listed for this class are options which may appear in each
7253  * individual column definition.
7254  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7255  * @constructor
7256  * @param {Object} config An Array of column config objects. See this class's
7257  * config objects for details.
7258 */
7259 Roo.grid.ColumnModel = function(config){
7260         /**
7261      * The config passed into the constructor
7262      */
7263     this.config = config;
7264     this.lookup = {};
7265
7266     // if no id, create one
7267     // if the column does not have a dataIndex mapping,
7268     // map it to the order it is in the config
7269     for(var i = 0, len = config.length; i < len; i++){
7270         var c = config[i];
7271         if(typeof c.dataIndex == "undefined"){
7272             c.dataIndex = i;
7273         }
7274         if(typeof c.renderer == "string"){
7275             c.renderer = Roo.util.Format[c.renderer];
7276         }
7277         if(typeof c.id == "undefined"){
7278             c.id = Roo.id();
7279         }
7280         if(c.editor && c.editor.xtype){
7281             c.editor  = Roo.factory(c.editor, Roo.grid);
7282         }
7283         if(c.editor && c.editor.isFormField){
7284             c.editor = new Roo.grid.GridEditor(c.editor);
7285         }
7286         this.lookup[c.id] = c;
7287     }
7288
7289     /**
7290      * The width of columns which have no width specified (defaults to 100)
7291      * @type Number
7292      */
7293     this.defaultWidth = 100;
7294
7295     /**
7296      * Default sortable of columns which have no sortable specified (defaults to false)
7297      * @type Boolean
7298      */
7299     this.defaultSortable = false;
7300
7301     this.addEvents({
7302         /**
7303              * @event widthchange
7304              * Fires when the width of a column changes.
7305              * @param {ColumnModel} this
7306              * @param {Number} columnIndex The column index
7307              * @param {Number} newWidth The new width
7308              */
7309             "widthchange": true,
7310         /**
7311              * @event headerchange
7312              * Fires when the text of a header changes.
7313              * @param {ColumnModel} this
7314              * @param {Number} columnIndex The column index
7315              * @param {Number} newText The new header text
7316              */
7317             "headerchange": true,
7318         /**
7319              * @event hiddenchange
7320              * Fires when a column is hidden or "unhidden".
7321              * @param {ColumnModel} this
7322              * @param {Number} columnIndex The column index
7323              * @param {Boolean} hidden true if hidden, false otherwise
7324              */
7325             "hiddenchange": true,
7326             /**
7327          * @event columnmoved
7328          * Fires when a column is moved.
7329          * @param {ColumnModel} this
7330          * @param {Number} oldIndex
7331          * @param {Number} newIndex
7332          */
7333         "columnmoved" : true,
7334         /**
7335          * @event columlockchange
7336          * Fires when a column's locked state is changed
7337          * @param {ColumnModel} this
7338          * @param {Number} colIndex
7339          * @param {Boolean} locked true if locked
7340          */
7341         "columnlockchange" : true
7342     });
7343     Roo.grid.ColumnModel.superclass.constructor.call(this);
7344 };
7345 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7346     /**
7347      * @cfg {String} header The header text to display in the Grid view.
7348      */
7349     /**
7350      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7351      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7352      * specified, the column's index is used as an index into the Record's data Array.
7353      */
7354     /**
7355      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7356      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7357      */
7358     /**
7359      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7360      * Defaults to the value of the {@link #defaultSortable} property.
7361      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7362      */
7363     /**
7364      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7365      */
7366     /**
7367      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7368      */
7369     /**
7370      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7371      */
7372     /**
7373      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7374      */
7375     /**
7376      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7377      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7378      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7379      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7380      */
7381        /**
7382      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7383      */
7384     /**
7385      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7386      */
7387     /**
7388      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7389      */
7390     /**
7391      * @cfg {String} cursor (Optional)
7392      */
7393     /**
7394      * @cfg {String} tooltip (Optional)
7395      */
7396     /**
7397      * @cfg {Number} xs (Optional)
7398      */
7399     /**
7400      * @cfg {Number} sm (Optional)
7401      */
7402     /**
7403      * @cfg {Number} md (Optional)
7404      */
7405     /**
7406      * @cfg {Number} lg (Optional)
7407      */
7408     /**
7409      * Returns the id of the column at the specified index.
7410      * @param {Number} index The column index
7411      * @return {String} the id
7412      */
7413     getColumnId : function(index){
7414         return this.config[index].id;
7415     },
7416
7417     /**
7418      * Returns the column for a specified id.
7419      * @param {String} id The column id
7420      * @return {Object} the column
7421      */
7422     getColumnById : function(id){
7423         return this.lookup[id];
7424     },
7425
7426     
7427     /**
7428      * Returns the column for a specified dataIndex.
7429      * @param {String} dataIndex The column dataIndex
7430      * @return {Object|Boolean} the column or false if not found
7431      */
7432     getColumnByDataIndex: function(dataIndex){
7433         var index = this.findColumnIndex(dataIndex);
7434         return index > -1 ? this.config[index] : false;
7435     },
7436     
7437     /**
7438      * Returns the index for a specified column id.
7439      * @param {String} id The column id
7440      * @return {Number} the index, or -1 if not found
7441      */
7442     getIndexById : function(id){
7443         for(var i = 0, len = this.config.length; i < len; i++){
7444             if(this.config[i].id == id){
7445                 return i;
7446             }
7447         }
7448         return -1;
7449     },
7450     
7451     /**
7452      * Returns the index for a specified column dataIndex.
7453      * @param {String} dataIndex The column dataIndex
7454      * @return {Number} the index, or -1 if not found
7455      */
7456     
7457     findColumnIndex : function(dataIndex){
7458         for(var i = 0, len = this.config.length; i < len; i++){
7459             if(this.config[i].dataIndex == dataIndex){
7460                 return i;
7461             }
7462         }
7463         return -1;
7464     },
7465     
7466     
7467     moveColumn : function(oldIndex, newIndex){
7468         var c = this.config[oldIndex];
7469         this.config.splice(oldIndex, 1);
7470         this.config.splice(newIndex, 0, c);
7471         this.dataMap = null;
7472         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7473     },
7474
7475     isLocked : function(colIndex){
7476         return this.config[colIndex].locked === true;
7477     },
7478
7479     setLocked : function(colIndex, value, suppressEvent){
7480         if(this.isLocked(colIndex) == value){
7481             return;
7482         }
7483         this.config[colIndex].locked = value;
7484         if(!suppressEvent){
7485             this.fireEvent("columnlockchange", this, colIndex, value);
7486         }
7487     },
7488
7489     getTotalLockedWidth : function(){
7490         var totalWidth = 0;
7491         for(var i = 0; i < this.config.length; i++){
7492             if(this.isLocked(i) && !this.isHidden(i)){
7493                 this.totalWidth += this.getColumnWidth(i);
7494             }
7495         }
7496         return totalWidth;
7497     },
7498
7499     getLockedCount : function(){
7500         for(var i = 0, len = this.config.length; i < len; i++){
7501             if(!this.isLocked(i)){
7502                 return i;
7503             }
7504         }
7505         
7506         return this.config.length;
7507     },
7508
7509     /**
7510      * Returns the number of columns.
7511      * @return {Number}
7512      */
7513     getColumnCount : function(visibleOnly){
7514         if(visibleOnly === true){
7515             var c = 0;
7516             for(var i = 0, len = this.config.length; i < len; i++){
7517                 if(!this.isHidden(i)){
7518                     c++;
7519                 }
7520             }
7521             return c;
7522         }
7523         return this.config.length;
7524     },
7525
7526     /**
7527      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7528      * @param {Function} fn
7529      * @param {Object} scope (optional)
7530      * @return {Array} result
7531      */
7532     getColumnsBy : function(fn, scope){
7533         var r = [];
7534         for(var i = 0, len = this.config.length; i < len; i++){
7535             var c = this.config[i];
7536             if(fn.call(scope||this, c, i) === true){
7537                 r[r.length] = c;
7538             }
7539         }
7540         return r;
7541     },
7542
7543     /**
7544      * Returns true if the specified column is sortable.
7545      * @param {Number} col The column index
7546      * @return {Boolean}
7547      */
7548     isSortable : function(col){
7549         if(typeof this.config[col].sortable == "undefined"){
7550             return this.defaultSortable;
7551         }
7552         return this.config[col].sortable;
7553     },
7554
7555     /**
7556      * Returns the rendering (formatting) function defined for the column.
7557      * @param {Number} col The column index.
7558      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7559      */
7560     getRenderer : function(col){
7561         if(!this.config[col].renderer){
7562             return Roo.grid.ColumnModel.defaultRenderer;
7563         }
7564         return this.config[col].renderer;
7565     },
7566
7567     /**
7568      * Sets the rendering (formatting) function for a column.
7569      * @param {Number} col The column index
7570      * @param {Function} fn The function to use to process the cell's raw data
7571      * to return HTML markup for the grid view. The render function is called with
7572      * the following parameters:<ul>
7573      * <li>Data value.</li>
7574      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7575      * <li>css A CSS style string to apply to the table cell.</li>
7576      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7577      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7578      * <li>Row index</li>
7579      * <li>Column index</li>
7580      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7581      */
7582     setRenderer : function(col, fn){
7583         this.config[col].renderer = fn;
7584     },
7585
7586     /**
7587      * Returns the width for the specified column.
7588      * @param {Number} col The column index
7589      * @return {Number}
7590      */
7591     getColumnWidth : function(col){
7592         return this.config[col].width * 1 || this.defaultWidth;
7593     },
7594
7595     /**
7596      * Sets the width for a column.
7597      * @param {Number} col The column index
7598      * @param {Number} width The new width
7599      */
7600     setColumnWidth : function(col, width, suppressEvent){
7601         this.config[col].width = width;
7602         this.totalWidth = null;
7603         if(!suppressEvent){
7604              this.fireEvent("widthchange", this, col, width);
7605         }
7606     },
7607
7608     /**
7609      * Returns the total width of all columns.
7610      * @param {Boolean} includeHidden True to include hidden column widths
7611      * @return {Number}
7612      */
7613     getTotalWidth : function(includeHidden){
7614         if(!this.totalWidth){
7615             this.totalWidth = 0;
7616             for(var i = 0, len = this.config.length; i < len; i++){
7617                 if(includeHidden || !this.isHidden(i)){
7618                     this.totalWidth += this.getColumnWidth(i);
7619                 }
7620             }
7621         }
7622         return this.totalWidth;
7623     },
7624
7625     /**
7626      * Returns the header for the specified column.
7627      * @param {Number} col The column index
7628      * @return {String}
7629      */
7630     getColumnHeader : function(col){
7631         return this.config[col].header;
7632     },
7633
7634     /**
7635      * Sets the header for a column.
7636      * @param {Number} col The column index
7637      * @param {String} header The new header
7638      */
7639     setColumnHeader : function(col, header){
7640         this.config[col].header = header;
7641         this.fireEvent("headerchange", this, col, header);
7642     },
7643
7644     /**
7645      * Returns the tooltip for the specified column.
7646      * @param {Number} col The column index
7647      * @return {String}
7648      */
7649     getColumnTooltip : function(col){
7650             return this.config[col].tooltip;
7651     },
7652     /**
7653      * Sets the tooltip for a column.
7654      * @param {Number} col The column index
7655      * @param {String} tooltip The new tooltip
7656      */
7657     setColumnTooltip : function(col, tooltip){
7658             this.config[col].tooltip = tooltip;
7659     },
7660
7661     /**
7662      * Returns the dataIndex for the specified column.
7663      * @param {Number} col The column index
7664      * @return {Number}
7665      */
7666     getDataIndex : function(col){
7667         return this.config[col].dataIndex;
7668     },
7669
7670     /**
7671      * Sets the dataIndex for a column.
7672      * @param {Number} col The column index
7673      * @param {Number} dataIndex The new dataIndex
7674      */
7675     setDataIndex : function(col, dataIndex){
7676         this.config[col].dataIndex = dataIndex;
7677     },
7678
7679     
7680     
7681     /**
7682      * Returns true if the cell is editable.
7683      * @param {Number} colIndex The column index
7684      * @param {Number} rowIndex The row index - this is nto actually used..?
7685      * @return {Boolean}
7686      */
7687     isCellEditable : function(colIndex, rowIndex){
7688         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7689     },
7690
7691     /**
7692      * Returns the editor defined for the cell/column.
7693      * return false or null to disable editing.
7694      * @param {Number} colIndex The column index
7695      * @param {Number} rowIndex The row index
7696      * @return {Object}
7697      */
7698     getCellEditor : function(colIndex, rowIndex){
7699         return this.config[colIndex].editor;
7700     },
7701
7702     /**
7703      * Sets if a column is editable.
7704      * @param {Number} col The column index
7705      * @param {Boolean} editable True if the column is editable
7706      */
7707     setEditable : function(col, editable){
7708         this.config[col].editable = editable;
7709     },
7710
7711
7712     /**
7713      * Returns true if the column is hidden.
7714      * @param {Number} colIndex The column index
7715      * @return {Boolean}
7716      */
7717     isHidden : function(colIndex){
7718         return this.config[colIndex].hidden;
7719     },
7720
7721
7722     /**
7723      * Returns true if the column width cannot be changed
7724      */
7725     isFixed : function(colIndex){
7726         return this.config[colIndex].fixed;
7727     },
7728
7729     /**
7730      * Returns true if the column can be resized
7731      * @return {Boolean}
7732      */
7733     isResizable : function(colIndex){
7734         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7735     },
7736     /**
7737      * Sets if a column is hidden.
7738      * @param {Number} colIndex The column index
7739      * @param {Boolean} hidden True if the column is hidden
7740      */
7741     setHidden : function(colIndex, hidden){
7742         this.config[colIndex].hidden = hidden;
7743         this.totalWidth = null;
7744         this.fireEvent("hiddenchange", this, colIndex, hidden);
7745     },
7746
7747     /**
7748      * Sets the editor for a column.
7749      * @param {Number} col The column index
7750      * @param {Object} editor The editor object
7751      */
7752     setEditor : function(col, editor){
7753         this.config[col].editor = editor;
7754     }
7755 });
7756
7757 Roo.grid.ColumnModel.defaultRenderer = function(value)
7758 {
7759     if(typeof value == "object") {
7760         return value;
7761     }
7762         if(typeof value == "string" && value.length < 1){
7763             return "&#160;";
7764         }
7765     
7766         return String.format("{0}", value);
7767 };
7768
7769 // Alias for backwards compatibility
7770 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7771 /*
7772  * Based on:
7773  * Ext JS Library 1.1.1
7774  * Copyright(c) 2006-2007, Ext JS, LLC.
7775  *
7776  * Originally Released Under LGPL - original licence link has changed is not relivant.
7777  *
7778  * Fork - LGPL
7779  * <script type="text/javascript">
7780  */
7781  
7782 /**
7783  * @class Roo.LoadMask
7784  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7785  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7786  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7787  * element's UpdateManager load indicator and will be destroyed after the initial load.
7788  * @constructor
7789  * Create a new LoadMask
7790  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7791  * @param {Object} config The config object
7792  */
7793 Roo.LoadMask = function(el, config){
7794     this.el = Roo.get(el);
7795     Roo.apply(this, config);
7796     if(this.store){
7797         this.store.on('beforeload', this.onBeforeLoad, this);
7798         this.store.on('load', this.onLoad, this);
7799         this.store.on('loadexception', this.onLoadException, this);
7800         this.removeMask = false;
7801     }else{
7802         var um = this.el.getUpdateManager();
7803         um.showLoadIndicator = false; // disable the default indicator
7804         um.on('beforeupdate', this.onBeforeLoad, this);
7805         um.on('update', this.onLoad, this);
7806         um.on('failure', this.onLoad, this);
7807         this.removeMask = true;
7808     }
7809 };
7810
7811 Roo.LoadMask.prototype = {
7812     /**
7813      * @cfg {Boolean} removeMask
7814      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7815      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7816      */
7817     /**
7818      * @cfg {String} msg
7819      * The text to display in a centered loading message box (defaults to 'Loading...')
7820      */
7821     msg : 'Loading...',
7822     /**
7823      * @cfg {String} msgCls
7824      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7825      */
7826     msgCls : 'x-mask-loading',
7827
7828     /**
7829      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7830      * @type Boolean
7831      */
7832     disabled: false,
7833
7834     /**
7835      * Disables the mask to prevent it from being displayed
7836      */
7837     disable : function(){
7838        this.disabled = true;
7839     },
7840
7841     /**
7842      * Enables the mask so that it can be displayed
7843      */
7844     enable : function(){
7845         this.disabled = false;
7846     },
7847     
7848     onLoadException : function()
7849     {
7850         Roo.log(arguments);
7851         
7852         if (typeof(arguments[3]) != 'undefined') {
7853             Roo.MessageBox.alert("Error loading",arguments[3]);
7854         } 
7855         /*
7856         try {
7857             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7858                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7859             }   
7860         } catch(e) {
7861             
7862         }
7863         */
7864     
7865         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7866     },
7867     // private
7868     onLoad : function()
7869     {
7870         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7871     },
7872
7873     // private
7874     onBeforeLoad : function(){
7875         if(!this.disabled){
7876             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7877         }
7878     },
7879
7880     // private
7881     destroy : function(){
7882         if(this.store){
7883             this.store.un('beforeload', this.onBeforeLoad, this);
7884             this.store.un('load', this.onLoad, this);
7885             this.store.un('loadexception', this.onLoadException, this);
7886         }else{
7887             var um = this.el.getUpdateManager();
7888             um.un('beforeupdate', this.onBeforeLoad, this);
7889             um.un('update', this.onLoad, this);
7890             um.un('failure', this.onLoad, this);
7891         }
7892     }
7893 };/*
7894  * - LGPL
7895  *
7896  * table
7897  * 
7898  */
7899
7900 /**
7901  * @class Roo.bootstrap.Table
7902  * @extends Roo.bootstrap.Component
7903  * Bootstrap Table class
7904  * @cfg {String} cls table class
7905  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7906  * @cfg {String} bgcolor Specifies the background color for a table
7907  * @cfg {Number} border Specifies whether the table cells should have borders or not
7908  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7909  * @cfg {Number} cellspacing Specifies the space between cells
7910  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7911  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7912  * @cfg {String} sortable Specifies that the table should be sortable
7913  * @cfg {String} summary Specifies a summary of the content of a table
7914  * @cfg {Number} width Specifies the width of a table
7915  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7916  * 
7917  * @cfg {boolean} striped Should the rows be alternative striped
7918  * @cfg {boolean} bordered Add borders to the table
7919  * @cfg {boolean} hover Add hover highlighting
7920  * @cfg {boolean} condensed Format condensed
7921  * @cfg {boolean} responsive Format condensed
7922  * @cfg {Boolean} loadMask (true|false) default false
7923  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7924  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7925  * @cfg {Boolean} rowSelection (true|false) default false
7926  * @cfg {Boolean} cellSelection (true|false) default false
7927  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7928  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7929  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7930  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7931  
7932  * 
7933  * @constructor
7934  * Create a new Table
7935  * @param {Object} config The config object
7936  */
7937
7938 Roo.bootstrap.Table = function(config){
7939     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7940     
7941   
7942     
7943     // BC...
7944     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7945     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7946     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7947     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7948     
7949     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7950     if (this.sm) {
7951         this.sm.grid = this;
7952         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7953         this.sm = this.selModel;
7954         this.sm.xmodule = this.xmodule || false;
7955     }
7956     
7957     if (this.cm && typeof(this.cm.config) == 'undefined') {
7958         this.colModel = new Roo.grid.ColumnModel(this.cm);
7959         this.cm = this.colModel;
7960         this.cm.xmodule = this.xmodule || false;
7961     }
7962     if (this.store) {
7963         this.store= Roo.factory(this.store, Roo.data);
7964         this.ds = this.store;
7965         this.ds.xmodule = this.xmodule || false;
7966          
7967     }
7968     if (this.footer && this.store) {
7969         this.footer.dataSource = this.ds;
7970         this.footer = Roo.factory(this.footer);
7971     }
7972     
7973     /** @private */
7974     this.addEvents({
7975         /**
7976          * @event cellclick
7977          * Fires when a cell is clicked
7978          * @param {Roo.bootstrap.Table} this
7979          * @param {Roo.Element} el
7980          * @param {Number} rowIndex
7981          * @param {Number} columnIndex
7982          * @param {Roo.EventObject} e
7983          */
7984         "cellclick" : true,
7985         /**
7986          * @event celldblclick
7987          * Fires when a cell is double clicked
7988          * @param {Roo.bootstrap.Table} this
7989          * @param {Roo.Element} el
7990          * @param {Number} rowIndex
7991          * @param {Number} columnIndex
7992          * @param {Roo.EventObject} e
7993          */
7994         "celldblclick" : true,
7995         /**
7996          * @event rowclick
7997          * Fires when a row is clicked
7998          * @param {Roo.bootstrap.Table} this
7999          * @param {Roo.Element} el
8000          * @param {Number} rowIndex
8001          * @param {Roo.EventObject} e
8002          */
8003         "rowclick" : true,
8004         /**
8005          * @event rowdblclick
8006          * Fires when a row is double clicked
8007          * @param {Roo.bootstrap.Table} this
8008          * @param {Roo.Element} el
8009          * @param {Number} rowIndex
8010          * @param {Roo.EventObject} e
8011          */
8012         "rowdblclick" : true,
8013         /**
8014          * @event mouseover
8015          * Fires when a mouseover occur
8016          * @param {Roo.bootstrap.Table} this
8017          * @param {Roo.Element} el
8018          * @param {Number} rowIndex
8019          * @param {Number} columnIndex
8020          * @param {Roo.EventObject} e
8021          */
8022         "mouseover" : true,
8023         /**
8024          * @event mouseout
8025          * Fires when a mouseout occur
8026          * @param {Roo.bootstrap.Table} this
8027          * @param {Roo.Element} el
8028          * @param {Number} rowIndex
8029          * @param {Number} columnIndex
8030          * @param {Roo.EventObject} e
8031          */
8032         "mouseout" : true,
8033         /**
8034          * @event rowclass
8035          * Fires when a row is rendered, so you can change add a style to it.
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8038          */
8039         'rowclass' : true,
8040           /**
8041          * @event rowsrendered
8042          * Fires when all the  rows have been rendered
8043          * @param {Roo.bootstrap.Table} this
8044          */
8045         'rowsrendered' : true,
8046         /**
8047          * @event contextmenu
8048          * The raw contextmenu event for the entire grid.
8049          * @param {Roo.EventObject} e
8050          */
8051         "contextmenu" : true,
8052         /**
8053          * @event rowcontextmenu
8054          * Fires when a row is right clicked
8055          * @param {Roo.bootstrap.Table} this
8056          * @param {Number} rowIndex
8057          * @param {Roo.EventObject} e
8058          */
8059         "rowcontextmenu" : true,
8060         /**
8061          * @event cellcontextmenu
8062          * Fires when a cell is right clicked
8063          * @param {Roo.bootstrap.Table} this
8064          * @param {Number} rowIndex
8065          * @param {Number} cellIndex
8066          * @param {Roo.EventObject} e
8067          */
8068          "cellcontextmenu" : true,
8069          /**
8070          * @event headercontextmenu
8071          * Fires when a header is right clicked
8072          * @param {Roo.bootstrap.Table} this
8073          * @param {Number} columnIndex
8074          * @param {Roo.EventObject} e
8075          */
8076         "headercontextmenu" : true
8077     });
8078 };
8079
8080 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8081     
8082     cls: false,
8083     align: false,
8084     bgcolor: false,
8085     border: false,
8086     cellpadding: false,
8087     cellspacing: false,
8088     frame: false,
8089     rules: false,
8090     sortable: false,
8091     summary: false,
8092     width: false,
8093     striped : false,
8094     scrollBody : false,
8095     bordered: false,
8096     hover:  false,
8097     condensed : false,
8098     responsive : false,
8099     sm : false,
8100     cm : false,
8101     store : false,
8102     loadMask : false,
8103     footerShow : true,
8104     headerShow : true,
8105   
8106     rowSelection : false,
8107     cellSelection : false,
8108     layout : false,
8109     
8110     // Roo.Element - the tbody
8111     mainBody: false,
8112     // Roo.Element - thead element
8113     mainHead: false,
8114     
8115     container: false, // used by gridpanel...
8116     
8117     lazyLoad : false,
8118     
8119     CSS : Roo.util.CSS,
8120     
8121     auto_hide_footer : false,
8122     
8123     getAutoCreate : function()
8124     {
8125         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8126         
8127         cfg = {
8128             tag: 'table',
8129             cls : 'table',
8130             cn : []
8131         };
8132         if (this.scrollBody) {
8133             cfg.cls += ' table-body-fixed';
8134         }    
8135         if (this.striped) {
8136             cfg.cls += ' table-striped';
8137         }
8138         
8139         if (this.hover) {
8140             cfg.cls += ' table-hover';
8141         }
8142         if (this.bordered) {
8143             cfg.cls += ' table-bordered';
8144         }
8145         if (this.condensed) {
8146             cfg.cls += ' table-condensed';
8147         }
8148         if (this.responsive) {
8149             cfg.cls += ' table-responsive';
8150         }
8151         
8152         if (this.cls) {
8153             cfg.cls+=  ' ' +this.cls;
8154         }
8155         
8156         // this lot should be simplifed...
8157         var _t = this;
8158         var cp = [
8159             'align',
8160             'bgcolor',
8161             'border',
8162             'cellpadding',
8163             'cellspacing',
8164             'frame',
8165             'rules',
8166             'sortable',
8167             'summary',
8168             'width'
8169         ].forEach(function(k) {
8170             if (_t[k]) {
8171                 cfg[k] = _t[k];
8172             }
8173         });
8174         
8175         
8176         if (this.layout) {
8177             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8178         }
8179         
8180         if(this.store || this.cm){
8181             if(this.headerShow){
8182                 cfg.cn.push(this.renderHeader());
8183             }
8184             
8185             cfg.cn.push(this.renderBody());
8186             
8187             if(this.footerShow){
8188                 cfg.cn.push(this.renderFooter());
8189             }
8190             // where does this come from?
8191             //cfg.cls+=  ' TableGrid';
8192         }
8193         
8194         return { cn : [ cfg ] };
8195     },
8196     
8197     initEvents : function()
8198     {   
8199         if(!this.store || !this.cm){
8200             return;
8201         }
8202         if (this.selModel) {
8203             this.selModel.initEvents();
8204         }
8205         
8206         
8207         //Roo.log('initEvents with ds!!!!');
8208         
8209         this.mainBody = this.el.select('tbody', true).first();
8210         this.mainHead = this.el.select('thead', true).first();
8211         this.mainFoot = this.el.select('tfoot', true).first();
8212         
8213         
8214         
8215         var _this = this;
8216         
8217         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8218             e.on('click', _this.sort, _this);
8219         });
8220         
8221         this.mainBody.on("click", this.onClick, this);
8222         this.mainBody.on("dblclick", this.onDblClick, this);
8223         
8224         // why is this done????? = it breaks dialogs??
8225         //this.parent().el.setStyle('position', 'relative');
8226         
8227         
8228         if (this.footer) {
8229             this.footer.parentId = this.id;
8230             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8231             
8232             if(this.lazyLoad){
8233                 this.el.select('tfoot tr td').first().addClass('hide');
8234             }
8235         } 
8236         
8237         if(this.loadMask) {
8238             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8239         }
8240         
8241         this.store.on('load', this.onLoad, this);
8242         this.store.on('beforeload', this.onBeforeLoad, this);
8243         this.store.on('update', this.onUpdate, this);
8244         this.store.on('add', this.onAdd, this);
8245         this.store.on("clear", this.clear, this);
8246         
8247         this.el.on("contextmenu", this.onContextMenu, this);
8248         
8249         this.mainBody.on('scroll', this.onBodyScroll, this);
8250         
8251         this.cm.on("headerchange", this.onHeaderChange, this);
8252         
8253         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8254         
8255     },
8256     
8257     onContextMenu : function(e, t)
8258     {
8259         this.processEvent("contextmenu", e);
8260     },
8261     
8262     processEvent : function(name, e)
8263     {
8264         if (name != 'touchstart' ) {
8265             this.fireEvent(name, e);    
8266         }
8267         
8268         var t = e.getTarget();
8269         
8270         var cell = Roo.get(t);
8271         
8272         if(!cell){
8273             return;
8274         }
8275         
8276         if(cell.findParent('tfoot', false, true)){
8277             return;
8278         }
8279         
8280         if(cell.findParent('thead', false, true)){
8281             
8282             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8283                 cell = Roo.get(t).findParent('th', false, true);
8284                 if (!cell) {
8285                     Roo.log("failed to find th in thead?");
8286                     Roo.log(e.getTarget());
8287                     return;
8288                 }
8289             }
8290             
8291             var cellIndex = cell.dom.cellIndex;
8292             
8293             var ename = name == 'touchstart' ? 'click' : name;
8294             this.fireEvent("header" + ename, this, cellIndex, e);
8295             
8296             return;
8297         }
8298         
8299         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8300             cell = Roo.get(t).findParent('td', false, true);
8301             if (!cell) {
8302                 Roo.log("failed to find th in tbody?");
8303                 Roo.log(e.getTarget());
8304                 return;
8305             }
8306         }
8307         
8308         var row = cell.findParent('tr', false, true);
8309         var cellIndex = cell.dom.cellIndex;
8310         var rowIndex = row.dom.rowIndex - 1;
8311         
8312         if(row !== false){
8313             
8314             this.fireEvent("row" + name, this, rowIndex, e);
8315             
8316             if(cell !== false){
8317             
8318                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8319             }
8320         }
8321         
8322     },
8323     
8324     onMouseover : function(e, el)
8325     {
8326         var cell = Roo.get(el);
8327         
8328         if(!cell){
8329             return;
8330         }
8331         
8332         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8333             cell = cell.findParent('td', false, true);
8334         }
8335         
8336         var row = cell.findParent('tr', false, true);
8337         var cellIndex = cell.dom.cellIndex;
8338         var rowIndex = row.dom.rowIndex - 1; // start from 0
8339         
8340         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8341         
8342     },
8343     
8344     onMouseout : function(e, el)
8345     {
8346         var cell = Roo.get(el);
8347         
8348         if(!cell){
8349             return;
8350         }
8351         
8352         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8353             cell = cell.findParent('td', false, true);
8354         }
8355         
8356         var row = cell.findParent('tr', false, true);
8357         var cellIndex = cell.dom.cellIndex;
8358         var rowIndex = row.dom.rowIndex - 1; // start from 0
8359         
8360         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8361         
8362     },
8363     
8364     onClick : function(e, el)
8365     {
8366         var cell = Roo.get(el);
8367         
8368         if(!cell || (!this.cellSelection && !this.rowSelection)){
8369             return;
8370         }
8371         
8372         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8373             cell = cell.findParent('td', false, true);
8374         }
8375         
8376         if(!cell || typeof(cell) == 'undefined'){
8377             return;
8378         }
8379         
8380         var row = cell.findParent('tr', false, true);
8381         
8382         if(!row || typeof(row) == 'undefined'){
8383             return;
8384         }
8385         
8386         var cellIndex = cell.dom.cellIndex;
8387         var rowIndex = this.getRowIndex(row);
8388         
8389         // why??? - should these not be based on SelectionModel?
8390         if(this.cellSelection){
8391             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8392         }
8393         
8394         if(this.rowSelection){
8395             this.fireEvent('rowclick', this, row, rowIndex, e);
8396         }
8397         
8398         
8399     },
8400         
8401     onDblClick : function(e,el)
8402     {
8403         var cell = Roo.get(el);
8404         
8405         if(!cell || (!this.cellSelection && !this.rowSelection)){
8406             return;
8407         }
8408         
8409         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410             cell = cell.findParent('td', false, true);
8411         }
8412         
8413         if(!cell || typeof(cell) == 'undefined'){
8414             return;
8415         }
8416         
8417         var row = cell.findParent('tr', false, true);
8418         
8419         if(!row || typeof(row) == 'undefined'){
8420             return;
8421         }
8422         
8423         var cellIndex = cell.dom.cellIndex;
8424         var rowIndex = this.getRowIndex(row);
8425         
8426         if(this.cellSelection){
8427             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8428         }
8429         
8430         if(this.rowSelection){
8431             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8432         }
8433     },
8434     
8435     sort : function(e,el)
8436     {
8437         var col = Roo.get(el);
8438         
8439         if(!col.hasClass('sortable')){
8440             return;
8441         }
8442         
8443         var sort = col.attr('sort');
8444         var dir = 'ASC';
8445         
8446         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8447             dir = 'DESC';
8448         }
8449         
8450         this.store.sortInfo = {field : sort, direction : dir};
8451         
8452         if (this.footer) {
8453             Roo.log("calling footer first");
8454             this.footer.onClick('first');
8455         } else {
8456         
8457             this.store.load({ params : { start : 0 } });
8458         }
8459     },
8460     
8461     renderHeader : function()
8462     {
8463         var header = {
8464             tag: 'thead',
8465             cn : []
8466         };
8467         
8468         var cm = this.cm;
8469         this.totalWidth = 0;
8470         
8471         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8472             
8473             var config = cm.config[i];
8474             
8475             var c = {
8476                 tag: 'th',
8477                 cls : 'x-hcol-' + i,
8478                 style : '',
8479                 html: cm.getColumnHeader(i)
8480             };
8481             
8482             var hh = '';
8483             
8484             if(typeof(config.sortable) != 'undefined' && config.sortable){
8485                 c.cls = 'sortable';
8486                 c.html = '<i class="glyphicon"></i>' + c.html;
8487             }
8488             
8489             // could use BS4 hidden-..-down 
8490             
8491             if(typeof(config.lgHeader) != 'undefined'){
8492                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8493             }
8494             
8495             if(typeof(config.mdHeader) != 'undefined'){
8496                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8497             }
8498             
8499             if(typeof(config.smHeader) != 'undefined'){
8500                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8501             }
8502             
8503             if(typeof(config.xsHeader) != 'undefined'){
8504                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8505             }
8506             
8507             if(hh.length){
8508                 c.html = hh;
8509             }
8510             
8511             if(typeof(config.tooltip) != 'undefined'){
8512                 c.tooltip = config.tooltip;
8513             }
8514             
8515             if(typeof(config.colspan) != 'undefined'){
8516                 c.colspan = config.colspan;
8517             }
8518             
8519             if(typeof(config.hidden) != 'undefined' && config.hidden){
8520                 c.style += ' display:none;';
8521             }
8522             
8523             if(typeof(config.dataIndex) != 'undefined'){
8524                 c.sort = config.dataIndex;
8525             }
8526             
8527            
8528             
8529             if(typeof(config.align) != 'undefined' && config.align.length){
8530                 c.style += ' text-align:' + config.align + ';';
8531             }
8532             
8533             if(typeof(config.width) != 'undefined'){
8534                 c.style += ' width:' + config.width + 'px;';
8535                 this.totalWidth += config.width;
8536             } else {
8537                 this.totalWidth += 100; // assume minimum of 100 per column?
8538             }
8539             
8540             if(typeof(config.cls) != 'undefined'){
8541                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8542             }
8543             
8544             ['xs','sm','md','lg'].map(function(size){
8545                 
8546                 if(typeof(config[size]) == 'undefined'){
8547                     return;
8548                 }
8549                  
8550                 if (!config[size]) { // 0 = hidden
8551                     // BS 4 '0' is treated as hide that column and below.
8552                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8553                     return;
8554                 }
8555                 
8556                 c.cls += ' col-' + size + '-' + config[size] + (
8557                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8558                 );
8559                 
8560                 
8561             });
8562             
8563             header.cn.push(c)
8564         }
8565         
8566         return header;
8567     },
8568     
8569     renderBody : function()
8570     {
8571         var body = {
8572             tag: 'tbody',
8573             cn : [
8574                 {
8575                     tag: 'tr',
8576                     cn : [
8577                         {
8578                             tag : 'td',
8579                             colspan :  this.cm.getColumnCount()
8580                         }
8581                     ]
8582                 }
8583             ]
8584         };
8585         
8586         return body;
8587     },
8588     
8589     renderFooter : function()
8590     {
8591         var footer = {
8592             tag: 'tfoot',
8593             cn : [
8594                 {
8595                     tag: 'tr',
8596                     cn : [
8597                         {
8598                             tag : 'td',
8599                             colspan :  this.cm.getColumnCount()
8600                         }
8601                     ]
8602                 }
8603             ]
8604         };
8605         
8606         return footer;
8607     },
8608     
8609     
8610     
8611     onLoad : function()
8612     {
8613 //        Roo.log('ds onload');
8614         this.clear();
8615         
8616         var _this = this;
8617         var cm = this.cm;
8618         var ds = this.store;
8619         
8620         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8621             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8622             if (_this.store.sortInfo) {
8623                     
8624                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8625                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8626                 }
8627                 
8628                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8629                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8630                 }
8631             }
8632         });
8633         
8634         var tbody =  this.mainBody;
8635               
8636         if(ds.getCount() > 0){
8637             ds.data.each(function(d,rowIndex){
8638                 var row =  this.renderRow(cm, ds, rowIndex);
8639                 
8640                 tbody.createChild(row);
8641                 
8642                 var _this = this;
8643                 
8644                 if(row.cellObjects.length){
8645                     Roo.each(row.cellObjects, function(r){
8646                         _this.renderCellObject(r);
8647                     })
8648                 }
8649                 
8650             }, this);
8651         }
8652         
8653         var tfoot = this.el.select('tfoot', true).first();
8654         
8655         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8656             
8657             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8658             
8659             var total = this.ds.getTotalCount();
8660             
8661             if(this.footer.pageSize < total){
8662                 this.mainFoot.show();
8663             }
8664         }
8665         
8666         Roo.each(this.el.select('tbody td', true).elements, function(e){
8667             e.on('mouseover', _this.onMouseover, _this);
8668         });
8669         
8670         Roo.each(this.el.select('tbody td', true).elements, function(e){
8671             e.on('mouseout', _this.onMouseout, _this);
8672         });
8673         this.fireEvent('rowsrendered', this);
8674         
8675         this.autoSize();
8676     },
8677     
8678     
8679     onUpdate : function(ds,record)
8680     {
8681         this.refreshRow(record);
8682         this.autoSize();
8683     },
8684     
8685     onRemove : function(ds, record, index, isUpdate){
8686         if(isUpdate !== true){
8687             this.fireEvent("beforerowremoved", this, index, record);
8688         }
8689         var bt = this.mainBody.dom;
8690         
8691         var rows = this.el.select('tbody > tr', true).elements;
8692         
8693         if(typeof(rows[index]) != 'undefined'){
8694             bt.removeChild(rows[index].dom);
8695         }
8696         
8697 //        if(bt.rows[index]){
8698 //            bt.removeChild(bt.rows[index]);
8699 //        }
8700         
8701         if(isUpdate !== true){
8702             //this.stripeRows(index);
8703             //this.syncRowHeights(index, index);
8704             //this.layout();
8705             this.fireEvent("rowremoved", this, index, record);
8706         }
8707     },
8708     
8709     onAdd : function(ds, records, rowIndex)
8710     {
8711         //Roo.log('on Add called');
8712         // - note this does not handle multiple adding very well..
8713         var bt = this.mainBody.dom;
8714         for (var i =0 ; i < records.length;i++) {
8715             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8716             //Roo.log(records[i]);
8717             //Roo.log(this.store.getAt(rowIndex+i));
8718             this.insertRow(this.store, rowIndex + i, false);
8719             return;
8720         }
8721         
8722     },
8723     
8724     
8725     refreshRow : function(record){
8726         var ds = this.store, index;
8727         if(typeof record == 'number'){
8728             index = record;
8729             record = ds.getAt(index);
8730         }else{
8731             index = ds.indexOf(record);
8732             if (index < 0) {
8733                 return; // should not happen - but seems to 
8734             }
8735         }
8736         this.insertRow(ds, index, true);
8737         this.autoSize();
8738         this.onRemove(ds, record, index+1, true);
8739         this.autoSize();
8740         //this.syncRowHeights(index, index);
8741         //this.layout();
8742         this.fireEvent("rowupdated", this, index, record);
8743     },
8744     
8745     insertRow : function(dm, rowIndex, isUpdate){
8746         
8747         if(!isUpdate){
8748             this.fireEvent("beforerowsinserted", this, rowIndex);
8749         }
8750             //var s = this.getScrollState();
8751         var row = this.renderRow(this.cm, this.store, rowIndex);
8752         // insert before rowIndex..
8753         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8754         
8755         var _this = this;
8756                 
8757         if(row.cellObjects.length){
8758             Roo.each(row.cellObjects, function(r){
8759                 _this.renderCellObject(r);
8760             })
8761         }
8762             
8763         if(!isUpdate){
8764             this.fireEvent("rowsinserted", this, rowIndex);
8765             //this.syncRowHeights(firstRow, lastRow);
8766             //this.stripeRows(firstRow);
8767             //this.layout();
8768         }
8769         
8770     },
8771     
8772     
8773     getRowDom : function(rowIndex)
8774     {
8775         var rows = this.el.select('tbody > tr', true).elements;
8776         
8777         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8778         
8779     },
8780     // returns the object tree for a tr..
8781   
8782     
8783     renderRow : function(cm, ds, rowIndex) 
8784     {
8785         var d = ds.getAt(rowIndex);
8786         
8787         var row = {
8788             tag : 'tr',
8789             cls : 'x-row-' + rowIndex,
8790             cn : []
8791         };
8792             
8793         var cellObjects = [];
8794         
8795         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8796             var config = cm.config[i];
8797             
8798             var renderer = cm.getRenderer(i);
8799             var value = '';
8800             var id = false;
8801             
8802             if(typeof(renderer) !== 'undefined'){
8803                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8804             }
8805             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8806             // and are rendered into the cells after the row is rendered - using the id for the element.
8807             
8808             if(typeof(value) === 'object'){
8809                 id = Roo.id();
8810                 cellObjects.push({
8811                     container : id,
8812                     cfg : value 
8813                 })
8814             }
8815             
8816             var rowcfg = {
8817                 record: d,
8818                 rowIndex : rowIndex,
8819                 colIndex : i,
8820                 rowClass : ''
8821             };
8822
8823             this.fireEvent('rowclass', this, rowcfg);
8824             
8825             var td = {
8826                 tag: 'td',
8827                 cls : rowcfg.rowClass + ' x-col-' + i,
8828                 style: '',
8829                 html: (typeof(value) === 'object') ? '' : value
8830             };
8831             
8832             if (id) {
8833                 td.id = id;
8834             }
8835             
8836             if(typeof(config.colspan) != 'undefined'){
8837                 td.colspan = config.colspan;
8838             }
8839             
8840             if(typeof(config.hidden) != 'undefined' && config.hidden){
8841                 td.style += ' display:none;';
8842             }
8843             
8844             if(typeof(config.align) != 'undefined' && config.align.length){
8845                 td.style += ' text-align:' + config.align + ';';
8846             }
8847             if(typeof(config.valign) != 'undefined' && config.valign.length){
8848                 td.style += ' vertical-align:' + config.valign + ';';
8849             }
8850             
8851             if(typeof(config.width) != 'undefined'){
8852                 td.style += ' width:' +  config.width + 'px;';
8853             }
8854             
8855             if(typeof(config.cursor) != 'undefined'){
8856                 td.style += ' cursor:' +  config.cursor + ';';
8857             }
8858             
8859             if(typeof(config.cls) != 'undefined'){
8860                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8861             }
8862             
8863             ['xs','sm','md','lg'].map(function(size){
8864                 
8865                 if(typeof(config[size]) == 'undefined'){
8866                     return;
8867                 }
8868                 
8869                 
8870                   
8871                 if (!config[size]) { // 0 = hidden
8872                     // BS 4 '0' is treated as hide that column and below.
8873                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8874                     return;
8875                 }
8876                 
8877                 td.cls += ' col-' + size + '-' + config[size] + (
8878                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8879                 );
8880                  
8881
8882             });
8883             
8884             row.cn.push(td);
8885            
8886         }
8887         
8888         row.cellObjects = cellObjects;
8889         
8890         return row;
8891           
8892     },
8893     
8894     
8895     
8896     onBeforeLoad : function()
8897     {
8898         
8899     },
8900      /**
8901      * Remove all rows
8902      */
8903     clear : function()
8904     {
8905         this.el.select('tbody', true).first().dom.innerHTML = '';
8906     },
8907     /**
8908      * Show or hide a row.
8909      * @param {Number} rowIndex to show or hide
8910      * @param {Boolean} state hide
8911      */
8912     setRowVisibility : function(rowIndex, state)
8913     {
8914         var bt = this.mainBody.dom;
8915         
8916         var rows = this.el.select('tbody > tr', true).elements;
8917         
8918         if(typeof(rows[rowIndex]) == 'undefined'){
8919             return;
8920         }
8921         rows[rowIndex].dom.style.display = state ? '' : 'none';
8922     },
8923     
8924     
8925     getSelectionModel : function(){
8926         if(!this.selModel){
8927             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8928         }
8929         return this.selModel;
8930     },
8931     /*
8932      * Render the Roo.bootstrap object from renderder
8933      */
8934     renderCellObject : function(r)
8935     {
8936         var _this = this;
8937         
8938         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8939         
8940         var t = r.cfg.render(r.container);
8941         
8942         if(r.cfg.cn){
8943             Roo.each(r.cfg.cn, function(c){
8944                 var child = {
8945                     container: t.getChildContainer(),
8946                     cfg: c
8947                 };
8948                 _this.renderCellObject(child);
8949             })
8950         }
8951     },
8952     
8953     getRowIndex : function(row)
8954     {
8955         var rowIndex = -1;
8956         
8957         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8958             if(el != row){
8959                 return;
8960             }
8961             
8962             rowIndex = index;
8963         });
8964         
8965         return rowIndex;
8966     },
8967      /**
8968      * Returns the grid's underlying element = used by panel.Grid
8969      * @return {Element} The element
8970      */
8971     getGridEl : function(){
8972         return this.el;
8973     },
8974      /**
8975      * Forces a resize - used by panel.Grid
8976      * @return {Element} The element
8977      */
8978     autoSize : function()
8979     {
8980         //var ctr = Roo.get(this.container.dom.parentElement);
8981         var ctr = Roo.get(this.el.dom);
8982         
8983         var thd = this.getGridEl().select('thead',true).first();
8984         var tbd = this.getGridEl().select('tbody', true).first();
8985         var tfd = this.getGridEl().select('tfoot', true).first();
8986         
8987         var cw = ctr.getWidth();
8988         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8989         
8990         if (tbd) {
8991             
8992             tbd.setWidth(ctr.getWidth());
8993             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8994             // this needs fixing for various usage - currently only hydra job advers I think..
8995             //tdb.setHeight(
8996             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8997             //); 
8998             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8999             cw -= barsize;
9000         }
9001         cw = Math.max(cw, this.totalWidth);
9002         this.getGridEl().select('tbody tr',true).setWidth(cw);
9003         
9004         // resize 'expandable coloumn?
9005         
9006         return; // we doe not have a view in this design..
9007         
9008     },
9009     onBodyScroll: function()
9010     {
9011         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9012         if(this.mainHead){
9013             this.mainHead.setStyle({
9014                 'position' : 'relative',
9015                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9016             });
9017         }
9018         
9019         if(this.lazyLoad){
9020             
9021             var scrollHeight = this.mainBody.dom.scrollHeight;
9022             
9023             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9024             
9025             var height = this.mainBody.getHeight();
9026             
9027             if(scrollHeight - height == scrollTop) {
9028                 
9029                 var total = this.ds.getTotalCount();
9030                 
9031                 if(this.footer.cursor + this.footer.pageSize < total){
9032                     
9033                     this.footer.ds.load({
9034                         params : {
9035                             start : this.footer.cursor + this.footer.pageSize,
9036                             limit : this.footer.pageSize
9037                         },
9038                         add : true
9039                     });
9040                 }
9041             }
9042             
9043         }
9044     },
9045     
9046     onHeaderChange : function()
9047     {
9048         var header = this.renderHeader();
9049         var table = this.el.select('table', true).first();
9050         
9051         this.mainHead.remove();
9052         this.mainHead = table.createChild(header, this.mainBody, false);
9053     },
9054     
9055     onHiddenChange : function(colModel, colIndex, hidden)
9056     {
9057         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9058         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9059         
9060         this.CSS.updateRule(thSelector, "display", "");
9061         this.CSS.updateRule(tdSelector, "display", "");
9062         
9063         if(hidden){
9064             this.CSS.updateRule(thSelector, "display", "none");
9065             this.CSS.updateRule(tdSelector, "display", "none");
9066         }
9067         
9068         this.onHeaderChange();
9069         this.onLoad();
9070     },
9071     
9072     setColumnWidth: function(col_index, width)
9073     {
9074         // width = "md-2 xs-2..."
9075         if(!this.colModel.config[col_index]) {
9076             return;
9077         }
9078         
9079         var w = width.split(" ");
9080         
9081         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9082         
9083         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9084         
9085         
9086         for(var j = 0; j < w.length; j++) {
9087             
9088             if(!w[j]) {
9089                 continue;
9090             }
9091             
9092             var size_cls = w[j].split("-");
9093             
9094             if(!Number.isInteger(size_cls[1] * 1)) {
9095                 continue;
9096             }
9097             
9098             if(!this.colModel.config[col_index][size_cls[0]]) {
9099                 continue;
9100             }
9101             
9102             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9103                 continue;
9104             }
9105             
9106             h_row[0].classList.replace(
9107                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9108                 "col-"+size_cls[0]+"-"+size_cls[1]
9109             );
9110             
9111             for(var i = 0; i < rows.length; i++) {
9112                 
9113                 var size_cls = w[j].split("-");
9114                 
9115                 if(!Number.isInteger(size_cls[1] * 1)) {
9116                     continue;
9117                 }
9118                 
9119                 if(!this.colModel.config[col_index][size_cls[0]]) {
9120                     continue;
9121                 }
9122                 
9123                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9124                     continue;
9125                 }
9126                 
9127                 rows[i].classList.replace(
9128                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9129                     "col-"+size_cls[0]+"-"+size_cls[1]
9130                 );
9131             }
9132             
9133             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9134         }
9135     }
9136 });
9137
9138  
9139
9140  /*
9141  * - LGPL
9142  *
9143  * table cell
9144  * 
9145  */
9146
9147 /**
9148  * @class Roo.bootstrap.TableCell
9149  * @extends Roo.bootstrap.Component
9150  * Bootstrap TableCell class
9151  * @cfg {String} html cell contain text
9152  * @cfg {String} cls cell class
9153  * @cfg {String} tag cell tag (td|th) default td
9154  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9155  * @cfg {String} align Aligns the content in a cell
9156  * @cfg {String} axis Categorizes cells
9157  * @cfg {String} bgcolor Specifies the background color of a cell
9158  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9159  * @cfg {Number} colspan Specifies the number of columns a cell should span
9160  * @cfg {String} headers Specifies one or more header cells a cell is related to
9161  * @cfg {Number} height Sets the height of a cell
9162  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9163  * @cfg {Number} rowspan Sets the number of rows a cell should span
9164  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9165  * @cfg {String} valign Vertical aligns the content in a cell
9166  * @cfg {Number} width Specifies the width of a cell
9167  * 
9168  * @constructor
9169  * Create a new TableCell
9170  * @param {Object} config The config object
9171  */
9172
9173 Roo.bootstrap.TableCell = function(config){
9174     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9175 };
9176
9177 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9178     
9179     html: false,
9180     cls: false,
9181     tag: false,
9182     abbr: false,
9183     align: false,
9184     axis: false,
9185     bgcolor: false,
9186     charoff: false,
9187     colspan: false,
9188     headers: false,
9189     height: false,
9190     nowrap: false,
9191     rowspan: false,
9192     scope: false,
9193     valign: false,
9194     width: false,
9195     
9196     
9197     getAutoCreate : function(){
9198         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9199         
9200         cfg = {
9201             tag: 'td'
9202         };
9203         
9204         if(this.tag){
9205             cfg.tag = this.tag;
9206         }
9207         
9208         if (this.html) {
9209             cfg.html=this.html
9210         }
9211         if (this.cls) {
9212             cfg.cls=this.cls
9213         }
9214         if (this.abbr) {
9215             cfg.abbr=this.abbr
9216         }
9217         if (this.align) {
9218             cfg.align=this.align
9219         }
9220         if (this.axis) {
9221             cfg.axis=this.axis
9222         }
9223         if (this.bgcolor) {
9224             cfg.bgcolor=this.bgcolor
9225         }
9226         if (this.charoff) {
9227             cfg.charoff=this.charoff
9228         }
9229         if (this.colspan) {
9230             cfg.colspan=this.colspan
9231         }
9232         if (this.headers) {
9233             cfg.headers=this.headers
9234         }
9235         if (this.height) {
9236             cfg.height=this.height
9237         }
9238         if (this.nowrap) {
9239             cfg.nowrap=this.nowrap
9240         }
9241         if (this.rowspan) {
9242             cfg.rowspan=this.rowspan
9243         }
9244         if (this.scope) {
9245             cfg.scope=this.scope
9246         }
9247         if (this.valign) {
9248             cfg.valign=this.valign
9249         }
9250         if (this.width) {
9251             cfg.width=this.width
9252         }
9253         
9254         
9255         return cfg;
9256     }
9257    
9258 });
9259
9260  
9261
9262  /*
9263  * - LGPL
9264  *
9265  * table row
9266  * 
9267  */
9268
9269 /**
9270  * @class Roo.bootstrap.TableRow
9271  * @extends Roo.bootstrap.Component
9272  * Bootstrap TableRow class
9273  * @cfg {String} cls row class
9274  * @cfg {String} align Aligns the content in a table row
9275  * @cfg {String} bgcolor Specifies a background color for a table row
9276  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9277  * @cfg {String} valign Vertical aligns the content in a table row
9278  * 
9279  * @constructor
9280  * Create a new TableRow
9281  * @param {Object} config The config object
9282  */
9283
9284 Roo.bootstrap.TableRow = function(config){
9285     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9286 };
9287
9288 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9289     
9290     cls: false,
9291     align: false,
9292     bgcolor: false,
9293     charoff: false,
9294     valign: false,
9295     
9296     getAutoCreate : function(){
9297         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9298         
9299         cfg = {
9300             tag: 'tr'
9301         };
9302             
9303         if(this.cls){
9304             cfg.cls = this.cls;
9305         }
9306         if(this.align){
9307             cfg.align = this.align;
9308         }
9309         if(this.bgcolor){
9310             cfg.bgcolor = this.bgcolor;
9311         }
9312         if(this.charoff){
9313             cfg.charoff = this.charoff;
9314         }
9315         if(this.valign){
9316             cfg.valign = this.valign;
9317         }
9318         
9319         return cfg;
9320     }
9321    
9322 });
9323
9324  
9325
9326  /*
9327  * - LGPL
9328  *
9329  * table body
9330  * 
9331  */
9332
9333 /**
9334  * @class Roo.bootstrap.TableBody
9335  * @extends Roo.bootstrap.Component
9336  * Bootstrap TableBody class
9337  * @cfg {String} cls element class
9338  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9339  * @cfg {String} align Aligns the content inside the element
9340  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9341  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9342  * 
9343  * @constructor
9344  * Create a new TableBody
9345  * @param {Object} config The config object
9346  */
9347
9348 Roo.bootstrap.TableBody = function(config){
9349     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9350 };
9351
9352 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9353     
9354     cls: false,
9355     tag: false,
9356     align: false,
9357     charoff: false,
9358     valign: false,
9359     
9360     getAutoCreate : function(){
9361         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9362         
9363         cfg = {
9364             tag: 'tbody'
9365         };
9366             
9367         if (this.cls) {
9368             cfg.cls=this.cls
9369         }
9370         if(this.tag){
9371             cfg.tag = this.tag;
9372         }
9373         
9374         if(this.align){
9375             cfg.align = this.align;
9376         }
9377         if(this.charoff){
9378             cfg.charoff = this.charoff;
9379         }
9380         if(this.valign){
9381             cfg.valign = this.valign;
9382         }
9383         
9384         return cfg;
9385     }
9386     
9387     
9388 //    initEvents : function()
9389 //    {
9390 //        
9391 //        if(!this.store){
9392 //            return;
9393 //        }
9394 //        
9395 //        this.store = Roo.factory(this.store, Roo.data);
9396 //        this.store.on('load', this.onLoad, this);
9397 //        
9398 //        this.store.load();
9399 //        
9400 //    },
9401 //    
9402 //    onLoad: function () 
9403 //    {   
9404 //        this.fireEvent('load', this);
9405 //    }
9406 //    
9407 //   
9408 });
9409
9410  
9411
9412  /*
9413  * Based on:
9414  * Ext JS Library 1.1.1
9415  * Copyright(c) 2006-2007, Ext JS, LLC.
9416  *
9417  * Originally Released Under LGPL - original licence link has changed is not relivant.
9418  *
9419  * Fork - LGPL
9420  * <script type="text/javascript">
9421  */
9422
9423 // as we use this in bootstrap.
9424 Roo.namespace('Roo.form');
9425  /**
9426  * @class Roo.form.Action
9427  * Internal Class used to handle form actions
9428  * @constructor
9429  * @param {Roo.form.BasicForm} el The form element or its id
9430  * @param {Object} config Configuration options
9431  */
9432
9433  
9434  
9435 // define the action interface
9436 Roo.form.Action = function(form, options){
9437     this.form = form;
9438     this.options = options || {};
9439 };
9440 /**
9441  * Client Validation Failed
9442  * @const 
9443  */
9444 Roo.form.Action.CLIENT_INVALID = 'client';
9445 /**
9446  * Server Validation Failed
9447  * @const 
9448  */
9449 Roo.form.Action.SERVER_INVALID = 'server';
9450  /**
9451  * Connect to Server Failed
9452  * @const 
9453  */
9454 Roo.form.Action.CONNECT_FAILURE = 'connect';
9455 /**
9456  * Reading Data from Server Failed
9457  * @const 
9458  */
9459 Roo.form.Action.LOAD_FAILURE = 'load';
9460
9461 Roo.form.Action.prototype = {
9462     type : 'default',
9463     failureType : undefined,
9464     response : undefined,
9465     result : undefined,
9466
9467     // interface method
9468     run : function(options){
9469
9470     },
9471
9472     // interface method
9473     success : function(response){
9474
9475     },
9476
9477     // interface method
9478     handleResponse : function(response){
9479
9480     },
9481
9482     // default connection failure
9483     failure : function(response){
9484         
9485         this.response = response;
9486         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9487         this.form.afterAction(this, false);
9488     },
9489
9490     processResponse : function(response){
9491         this.response = response;
9492         if(!response.responseText){
9493             return true;
9494         }
9495         this.result = this.handleResponse(response);
9496         return this.result;
9497     },
9498
9499     // utility functions used internally
9500     getUrl : function(appendParams){
9501         var url = this.options.url || this.form.url || this.form.el.dom.action;
9502         if(appendParams){
9503             var p = this.getParams();
9504             if(p){
9505                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9506             }
9507         }
9508         return url;
9509     },
9510
9511     getMethod : function(){
9512         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9513     },
9514
9515     getParams : function(){
9516         var bp = this.form.baseParams;
9517         var p = this.options.params;
9518         if(p){
9519             if(typeof p == "object"){
9520                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9521             }else if(typeof p == 'string' && bp){
9522                 p += '&' + Roo.urlEncode(bp);
9523             }
9524         }else if(bp){
9525             p = Roo.urlEncode(bp);
9526         }
9527         return p;
9528     },
9529
9530     createCallback : function(){
9531         return {
9532             success: this.success,
9533             failure: this.failure,
9534             scope: this,
9535             timeout: (this.form.timeout*1000),
9536             upload: this.form.fileUpload ? this.success : undefined
9537         };
9538     }
9539 };
9540
9541 Roo.form.Action.Submit = function(form, options){
9542     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9543 };
9544
9545 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9546     type : 'submit',
9547
9548     haveProgress : false,
9549     uploadComplete : false,
9550     
9551     // uploadProgress indicator.
9552     uploadProgress : function()
9553     {
9554         if (!this.form.progressUrl) {
9555             return;
9556         }
9557         
9558         if (!this.haveProgress) {
9559             Roo.MessageBox.progress("Uploading", "Uploading");
9560         }
9561         if (this.uploadComplete) {
9562            Roo.MessageBox.hide();
9563            return;
9564         }
9565         
9566         this.haveProgress = true;
9567    
9568         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9569         
9570         var c = new Roo.data.Connection();
9571         c.request({
9572             url : this.form.progressUrl,
9573             params: {
9574                 id : uid
9575             },
9576             method: 'GET',
9577             success : function(req){
9578                //console.log(data);
9579                 var rdata = false;
9580                 var edata;
9581                 try  {
9582                    rdata = Roo.decode(req.responseText)
9583                 } catch (e) {
9584                     Roo.log("Invalid data from server..");
9585                     Roo.log(edata);
9586                     return;
9587                 }
9588                 if (!rdata || !rdata.success) {
9589                     Roo.log(rdata);
9590                     Roo.MessageBox.alert(Roo.encode(rdata));
9591                     return;
9592                 }
9593                 var data = rdata.data;
9594                 
9595                 if (this.uploadComplete) {
9596                    Roo.MessageBox.hide();
9597                    return;
9598                 }
9599                    
9600                 if (data){
9601                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9602                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9603                     );
9604                 }
9605                 this.uploadProgress.defer(2000,this);
9606             },
9607        
9608             failure: function(data) {
9609                 Roo.log('progress url failed ');
9610                 Roo.log(data);
9611             },
9612             scope : this
9613         });
9614            
9615     },
9616     
9617     
9618     run : function()
9619     {
9620         // run get Values on the form, so it syncs any secondary forms.
9621         this.form.getValues();
9622         
9623         var o = this.options;
9624         var method = this.getMethod();
9625         var isPost = method == 'POST';
9626         if(o.clientValidation === false || this.form.isValid()){
9627             
9628             if (this.form.progressUrl) {
9629                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9630                     (new Date() * 1) + '' + Math.random());
9631                     
9632             } 
9633             
9634             
9635             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9636                 form:this.form.el.dom,
9637                 url:this.getUrl(!isPost),
9638                 method: method,
9639                 params:isPost ? this.getParams() : null,
9640                 isUpload: this.form.fileUpload,
9641                 formData : this.form.formData
9642             }));
9643             
9644             this.uploadProgress();
9645
9646         }else if (o.clientValidation !== false){ // client validation failed
9647             this.failureType = Roo.form.Action.CLIENT_INVALID;
9648             this.form.afterAction(this, false);
9649         }
9650     },
9651
9652     success : function(response)
9653     {
9654         this.uploadComplete= true;
9655         if (this.haveProgress) {
9656             Roo.MessageBox.hide();
9657         }
9658         
9659         
9660         var result = this.processResponse(response);
9661         if(result === true || result.success){
9662             this.form.afterAction(this, true);
9663             return;
9664         }
9665         if(result.errors){
9666             this.form.markInvalid(result.errors);
9667             this.failureType = Roo.form.Action.SERVER_INVALID;
9668         }
9669         this.form.afterAction(this, false);
9670     },
9671     failure : function(response)
9672     {
9673         this.uploadComplete= true;
9674         if (this.haveProgress) {
9675             Roo.MessageBox.hide();
9676         }
9677         
9678         this.response = response;
9679         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9680         this.form.afterAction(this, false);
9681     },
9682     
9683     handleResponse : function(response){
9684         if(this.form.errorReader){
9685             var rs = this.form.errorReader.read(response);
9686             var errors = [];
9687             if(rs.records){
9688                 for(var i = 0, len = rs.records.length; i < len; i++) {
9689                     var r = rs.records[i];
9690                     errors[i] = r.data;
9691                 }
9692             }
9693             if(errors.length < 1){
9694                 errors = null;
9695             }
9696             return {
9697                 success : rs.success,
9698                 errors : errors
9699             };
9700         }
9701         var ret = false;
9702         try {
9703             ret = Roo.decode(response.responseText);
9704         } catch (e) {
9705             ret = {
9706                 success: false,
9707                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9708                 errors : []
9709             };
9710         }
9711         return ret;
9712         
9713     }
9714 });
9715
9716
9717 Roo.form.Action.Load = function(form, options){
9718     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9719     this.reader = this.form.reader;
9720 };
9721
9722 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9723     type : 'load',
9724
9725     run : function(){
9726         
9727         Roo.Ajax.request(Roo.apply(
9728                 this.createCallback(), {
9729                     method:this.getMethod(),
9730                     url:this.getUrl(false),
9731                     params:this.getParams()
9732         }));
9733     },
9734
9735     success : function(response){
9736         
9737         var result = this.processResponse(response);
9738         if(result === true || !result.success || !result.data){
9739             this.failureType = Roo.form.Action.LOAD_FAILURE;
9740             this.form.afterAction(this, false);
9741             return;
9742         }
9743         this.form.clearInvalid();
9744         this.form.setValues(result.data);
9745         this.form.afterAction(this, true);
9746     },
9747
9748     handleResponse : function(response){
9749         if(this.form.reader){
9750             var rs = this.form.reader.read(response);
9751             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9752             return {
9753                 success : rs.success,
9754                 data : data
9755             };
9756         }
9757         return Roo.decode(response.responseText);
9758     }
9759 });
9760
9761 Roo.form.Action.ACTION_TYPES = {
9762     'load' : Roo.form.Action.Load,
9763     'submit' : Roo.form.Action.Submit
9764 };/*
9765  * - LGPL
9766  *
9767  * form
9768  *
9769  */
9770
9771 /**
9772  * @class Roo.bootstrap.Form
9773  * @extends Roo.bootstrap.Component
9774  * Bootstrap Form class
9775  * @cfg {String} method  GET | POST (default POST)
9776  * @cfg {String} labelAlign top | left (default top)
9777  * @cfg {String} align left  | right - for navbars
9778  * @cfg {Boolean} loadMask load mask when submit (default true)
9779
9780  *
9781  * @constructor
9782  * Create a new Form
9783  * @param {Object} config The config object
9784  */
9785
9786
9787 Roo.bootstrap.Form = function(config){
9788     
9789     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9790     
9791     Roo.bootstrap.Form.popover.apply();
9792     
9793     this.addEvents({
9794         /**
9795          * @event clientvalidation
9796          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9797          * @param {Form} this
9798          * @param {Boolean} valid true if the form has passed client-side validation
9799          */
9800         clientvalidation: true,
9801         /**
9802          * @event beforeaction
9803          * Fires before any action is performed. Return false to cancel the action.
9804          * @param {Form} this
9805          * @param {Action} action The action to be performed
9806          */
9807         beforeaction: true,
9808         /**
9809          * @event actionfailed
9810          * Fires when an action fails.
9811          * @param {Form} this
9812          * @param {Action} action The action that failed
9813          */
9814         actionfailed : true,
9815         /**
9816          * @event actioncomplete
9817          * Fires when an action is completed.
9818          * @param {Form} this
9819          * @param {Action} action The action that completed
9820          */
9821         actioncomplete : true
9822     });
9823 };
9824
9825 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9826
9827      /**
9828      * @cfg {String} method
9829      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9830      */
9831     method : 'POST',
9832     /**
9833      * @cfg {String} url
9834      * The URL to use for form actions if one isn't supplied in the action options.
9835      */
9836     /**
9837      * @cfg {Boolean} fileUpload
9838      * Set to true if this form is a file upload.
9839      */
9840
9841     /**
9842      * @cfg {Object} baseParams
9843      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9844      */
9845
9846     /**
9847      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9848      */
9849     timeout: 30,
9850     /**
9851      * @cfg {Sting} align (left|right) for navbar forms
9852      */
9853     align : 'left',
9854
9855     // private
9856     activeAction : null,
9857
9858     /**
9859      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9860      * element by passing it or its id or mask the form itself by passing in true.
9861      * @type Mixed
9862      */
9863     waitMsgTarget : false,
9864
9865     loadMask : true,
9866     
9867     /**
9868      * @cfg {Boolean} errorMask (true|false) default false
9869      */
9870     errorMask : false,
9871     
9872     /**
9873      * @cfg {Number} maskOffset Default 100
9874      */
9875     maskOffset : 100,
9876     
9877     /**
9878      * @cfg {Boolean} maskBody
9879      */
9880     maskBody : false,
9881
9882     getAutoCreate : function(){
9883
9884         var cfg = {
9885             tag: 'form',
9886             method : this.method || 'POST',
9887             id : this.id || Roo.id(),
9888             cls : ''
9889         };
9890         if (this.parent().xtype.match(/^Nav/)) {
9891             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9892
9893         }
9894
9895         if (this.labelAlign == 'left' ) {
9896             cfg.cls += ' form-horizontal';
9897         }
9898
9899
9900         return cfg;
9901     },
9902     initEvents : function()
9903     {
9904         this.el.on('submit', this.onSubmit, this);
9905         // this was added as random key presses on the form where triggering form submit.
9906         this.el.on('keypress', function(e) {
9907             if (e.getCharCode() != 13) {
9908                 return true;
9909             }
9910             // we might need to allow it for textareas.. and some other items.
9911             // check e.getTarget().
9912
9913             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9914                 return true;
9915             }
9916
9917             Roo.log("keypress blocked");
9918
9919             e.preventDefault();
9920             return false;
9921         });
9922         
9923     },
9924     // private
9925     onSubmit : function(e){
9926         e.stopEvent();
9927     },
9928
9929      /**
9930      * Returns true if client-side validation on the form is successful.
9931      * @return Boolean
9932      */
9933     isValid : function(){
9934         var items = this.getItems();
9935         var valid = true;
9936         var target = false;
9937         
9938         items.each(function(f){
9939             
9940             if(f.validate()){
9941                 return;
9942             }
9943             
9944             Roo.log('invalid field: ' + f.name);
9945             
9946             valid = false;
9947
9948             if(!target && f.el.isVisible(true)){
9949                 target = f;
9950             }
9951            
9952         });
9953         
9954         if(this.errorMask && !valid){
9955             Roo.bootstrap.Form.popover.mask(this, target);
9956         }
9957         
9958         return valid;
9959     },
9960     
9961     /**
9962      * Returns true if any fields in this form have changed since their original load.
9963      * @return Boolean
9964      */
9965     isDirty : function(){
9966         var dirty = false;
9967         var items = this.getItems();
9968         items.each(function(f){
9969            if(f.isDirty()){
9970                dirty = true;
9971                return false;
9972            }
9973            return true;
9974         });
9975         return dirty;
9976     },
9977      /**
9978      * Performs a predefined action (submit or load) or custom actions you define on this form.
9979      * @param {String} actionName The name of the action type
9980      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9981      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9982      * accept other config options):
9983      * <pre>
9984 Property          Type             Description
9985 ----------------  ---------------  ----------------------------------------------------------------------------------
9986 url               String           The url for the action (defaults to the form's url)
9987 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9988 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9989 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9990                                    validate the form on the client (defaults to false)
9991      * </pre>
9992      * @return {BasicForm} this
9993      */
9994     doAction : function(action, options){
9995         if(typeof action == 'string'){
9996             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9997         }
9998         if(this.fireEvent('beforeaction', this, action) !== false){
9999             this.beforeAction(action);
10000             action.run.defer(100, action);
10001         }
10002         return this;
10003     },
10004
10005     // private
10006     beforeAction : function(action){
10007         var o = action.options;
10008         
10009         if(this.loadMask){
10010             
10011             if(this.maskBody){
10012                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10013             } else {
10014                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10015             }
10016         }
10017         // not really supported yet.. ??
10018
10019         //if(this.waitMsgTarget === true){
10020         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10021         //}else if(this.waitMsgTarget){
10022         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10023         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10024         //}else {
10025         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10026        // }
10027
10028     },
10029
10030     // private
10031     afterAction : function(action, success){
10032         this.activeAction = null;
10033         var o = action.options;
10034
10035         if(this.loadMask){
10036             
10037             if(this.maskBody){
10038                 Roo.get(document.body).unmask();
10039             } else {
10040                 this.el.unmask();
10041             }
10042         }
10043         
10044         //if(this.waitMsgTarget === true){
10045 //            this.el.unmask();
10046         //}else if(this.waitMsgTarget){
10047         //    this.waitMsgTarget.unmask();
10048         //}else{
10049         //    Roo.MessageBox.updateProgress(1);
10050         //    Roo.MessageBox.hide();
10051        // }
10052         //
10053         if(success){
10054             if(o.reset){
10055                 this.reset();
10056             }
10057             Roo.callback(o.success, o.scope, [this, action]);
10058             this.fireEvent('actioncomplete', this, action);
10059
10060         }else{
10061
10062             // failure condition..
10063             // we have a scenario where updates need confirming.
10064             // eg. if a locking scenario exists..
10065             // we look for { errors : { needs_confirm : true }} in the response.
10066             if (
10067                 (typeof(action.result) != 'undefined')  &&
10068                 (typeof(action.result.errors) != 'undefined')  &&
10069                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10070            ){
10071                 var _t = this;
10072                 Roo.log("not supported yet");
10073                  /*
10074
10075                 Roo.MessageBox.confirm(
10076                     "Change requires confirmation",
10077                     action.result.errorMsg,
10078                     function(r) {
10079                         if (r != 'yes') {
10080                             return;
10081                         }
10082                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10083                     }
10084
10085                 );
10086                 */
10087
10088
10089                 return;
10090             }
10091
10092             Roo.callback(o.failure, o.scope, [this, action]);
10093             // show an error message if no failed handler is set..
10094             if (!this.hasListener('actionfailed')) {
10095                 Roo.log("need to add dialog support");
10096                 /*
10097                 Roo.MessageBox.alert("Error",
10098                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10099                         action.result.errorMsg :
10100                         "Saving Failed, please check your entries or try again"
10101                 );
10102                 */
10103             }
10104
10105             this.fireEvent('actionfailed', this, action);
10106         }
10107
10108     },
10109     /**
10110      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10111      * @param {String} id The value to search for
10112      * @return Field
10113      */
10114     findField : function(id){
10115         var items = this.getItems();
10116         var field = items.get(id);
10117         if(!field){
10118              items.each(function(f){
10119                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10120                     field = f;
10121                     return false;
10122                 }
10123                 return true;
10124             });
10125         }
10126         return field || null;
10127     },
10128      /**
10129      * Mark fields in this form invalid in bulk.
10130      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10131      * @return {BasicForm} this
10132      */
10133     markInvalid : function(errors){
10134         if(errors instanceof Array){
10135             for(var i = 0, len = errors.length; i < len; i++){
10136                 var fieldError = errors[i];
10137                 var f = this.findField(fieldError.id);
10138                 if(f){
10139                     f.markInvalid(fieldError.msg);
10140                 }
10141             }
10142         }else{
10143             var field, id;
10144             for(id in errors){
10145                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10146                     field.markInvalid(errors[id]);
10147                 }
10148             }
10149         }
10150         //Roo.each(this.childForms || [], function (f) {
10151         //    f.markInvalid(errors);
10152         //});
10153
10154         return this;
10155     },
10156
10157     /**
10158      * Set values for fields in this form in bulk.
10159      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10160      * @return {BasicForm} this
10161      */
10162     setValues : function(values){
10163         if(values instanceof Array){ // array of objects
10164             for(var i = 0, len = values.length; i < len; i++){
10165                 var v = values[i];
10166                 var f = this.findField(v.id);
10167                 if(f){
10168                     f.setValue(v.value);
10169                     if(this.trackResetOnLoad){
10170                         f.originalValue = f.getValue();
10171                     }
10172                 }
10173             }
10174         }else{ // object hash
10175             var field, id;
10176             for(id in values){
10177                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10178
10179                     if (field.setFromData &&
10180                         field.valueField &&
10181                         field.displayField &&
10182                         // combos' with local stores can
10183                         // be queried via setValue()
10184                         // to set their value..
10185                         (field.store && !field.store.isLocal)
10186                         ) {
10187                         // it's a combo
10188                         var sd = { };
10189                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10190                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10191                         field.setFromData(sd);
10192
10193                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10194                         
10195                         field.setFromData(values);
10196                         
10197                     } else {
10198                         field.setValue(values[id]);
10199                     }
10200
10201
10202                     if(this.trackResetOnLoad){
10203                         field.originalValue = field.getValue();
10204                     }
10205                 }
10206             }
10207         }
10208
10209         //Roo.each(this.childForms || [], function (f) {
10210         //    f.setValues(values);
10211         //});
10212
10213         return this;
10214     },
10215
10216     /**
10217      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10218      * they are returned as an array.
10219      * @param {Boolean} asString
10220      * @return {Object}
10221      */
10222     getValues : function(asString){
10223         //if (this.childForms) {
10224             // copy values from the child forms
10225         //    Roo.each(this.childForms, function (f) {
10226         //        this.setValues(f.getValues());
10227         //    }, this);
10228         //}
10229
10230
10231
10232         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10233         if(asString === true){
10234             return fs;
10235         }
10236         return Roo.urlDecode(fs);
10237     },
10238
10239     /**
10240      * Returns the fields in this form as an object with key/value pairs.
10241      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10242      * @return {Object}
10243      */
10244     getFieldValues : function(with_hidden)
10245     {
10246         var items = this.getItems();
10247         var ret = {};
10248         items.each(function(f){
10249             
10250             if (!f.getName()) {
10251                 return;
10252             }
10253             
10254             var v = f.getValue();
10255             
10256             if (f.inputType =='radio') {
10257                 if (typeof(ret[f.getName()]) == 'undefined') {
10258                     ret[f.getName()] = ''; // empty..
10259                 }
10260
10261                 if (!f.el.dom.checked) {
10262                     return;
10263
10264                 }
10265                 v = f.el.dom.value;
10266
10267             }
10268             
10269             if(f.xtype == 'MoneyField'){
10270                 ret[f.currencyName] = f.getCurrency();
10271             }
10272
10273             // not sure if this supported any more..
10274             if ((typeof(v) == 'object') && f.getRawValue) {
10275                 v = f.getRawValue() ; // dates..
10276             }
10277             // combo boxes where name != hiddenName...
10278             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10279                 ret[f.name] = f.getRawValue();
10280             }
10281             ret[f.getName()] = v;
10282         });
10283
10284         return ret;
10285     },
10286
10287     /**
10288      * Clears all invalid messages in this form.
10289      * @return {BasicForm} this
10290      */
10291     clearInvalid : function(){
10292         var items = this.getItems();
10293
10294         items.each(function(f){
10295            f.clearInvalid();
10296         });
10297
10298         return this;
10299     },
10300
10301     /**
10302      * Resets this form.
10303      * @return {BasicForm} this
10304      */
10305     reset : function(){
10306         var items = this.getItems();
10307         items.each(function(f){
10308             f.reset();
10309         });
10310
10311         Roo.each(this.childForms || [], function (f) {
10312             f.reset();
10313         });
10314
10315
10316         return this;
10317     },
10318     
10319     getItems : function()
10320     {
10321         var r=new Roo.util.MixedCollection(false, function(o){
10322             return o.id || (o.id = Roo.id());
10323         });
10324         var iter = function(el) {
10325             if (el.inputEl) {
10326                 r.add(el);
10327             }
10328             if (!el.items) {
10329                 return;
10330             }
10331             Roo.each(el.items,function(e) {
10332                 iter(e);
10333             });
10334         };
10335
10336         iter(this);
10337         return r;
10338     },
10339     
10340     hideFields : function(items)
10341     {
10342         Roo.each(items, function(i){
10343             
10344             var f = this.findField(i);
10345             
10346             if(!f){
10347                 return;
10348             }
10349             
10350             f.hide();
10351             
10352         }, this);
10353     },
10354     
10355     showFields : function(items)
10356     {
10357         Roo.each(items, function(i){
10358             
10359             var f = this.findField(i);
10360             
10361             if(!f){
10362                 return;
10363             }
10364             
10365             f.show();
10366             
10367         }, this);
10368     }
10369
10370 });
10371
10372 Roo.apply(Roo.bootstrap.Form, {
10373     
10374     popover : {
10375         
10376         padding : 5,
10377         
10378         isApplied : false,
10379         
10380         isMasked : false,
10381         
10382         form : false,
10383         
10384         target : false,
10385         
10386         toolTip : false,
10387         
10388         intervalID : false,
10389         
10390         maskEl : false,
10391         
10392         apply : function()
10393         {
10394             if(this.isApplied){
10395                 return;
10396             }
10397             
10398             this.maskEl = {
10399                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10400                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10401                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10402                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10403             };
10404             
10405             this.maskEl.top.enableDisplayMode("block");
10406             this.maskEl.left.enableDisplayMode("block");
10407             this.maskEl.bottom.enableDisplayMode("block");
10408             this.maskEl.right.enableDisplayMode("block");
10409             
10410             this.toolTip = new Roo.bootstrap.Tooltip({
10411                 cls : 'roo-form-error-popover',
10412                 alignment : {
10413                     'left' : ['r-l', [-2,0], 'right'],
10414                     'right' : ['l-r', [2,0], 'left'],
10415                     'bottom' : ['tl-bl', [0,2], 'top'],
10416                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10417                 }
10418             });
10419             
10420             this.toolTip.render(Roo.get(document.body));
10421
10422             this.toolTip.el.enableDisplayMode("block");
10423             
10424             Roo.get(document.body).on('click', function(){
10425                 this.unmask();
10426             }, this);
10427             
10428             Roo.get(document.body).on('touchstart', function(){
10429                 this.unmask();
10430             }, this);
10431             
10432             this.isApplied = true
10433         },
10434         
10435         mask : function(form, target)
10436         {
10437             this.form = form;
10438             
10439             this.target = target;
10440             
10441             if(!this.form.errorMask || !target.el){
10442                 return;
10443             }
10444             
10445             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10446             
10447             Roo.log(scrollable);
10448             
10449             var ot = this.target.el.calcOffsetsTo(scrollable);
10450             
10451             var scrollTo = ot[1] - this.form.maskOffset;
10452             
10453             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10454             
10455             scrollable.scrollTo('top', scrollTo);
10456             
10457             var box = this.target.el.getBox();
10458             Roo.log(box);
10459             var zIndex = Roo.bootstrap.Modal.zIndex++;
10460
10461             
10462             this.maskEl.top.setStyle('position', 'absolute');
10463             this.maskEl.top.setStyle('z-index', zIndex);
10464             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10465             this.maskEl.top.setLeft(0);
10466             this.maskEl.top.setTop(0);
10467             this.maskEl.top.show();
10468             
10469             this.maskEl.left.setStyle('position', 'absolute');
10470             this.maskEl.left.setStyle('z-index', zIndex);
10471             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10472             this.maskEl.left.setLeft(0);
10473             this.maskEl.left.setTop(box.y - this.padding);
10474             this.maskEl.left.show();
10475
10476             this.maskEl.bottom.setStyle('position', 'absolute');
10477             this.maskEl.bottom.setStyle('z-index', zIndex);
10478             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10479             this.maskEl.bottom.setLeft(0);
10480             this.maskEl.bottom.setTop(box.bottom + this.padding);
10481             this.maskEl.bottom.show();
10482
10483             this.maskEl.right.setStyle('position', 'absolute');
10484             this.maskEl.right.setStyle('z-index', zIndex);
10485             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10486             this.maskEl.right.setLeft(box.right + this.padding);
10487             this.maskEl.right.setTop(box.y - this.padding);
10488             this.maskEl.right.show();
10489
10490             this.toolTip.bindEl = this.target.el;
10491
10492             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10493
10494             var tip = this.target.blankText;
10495
10496             if(this.target.getValue() !== '' ) {
10497                 
10498                 if (this.target.invalidText.length) {
10499                     tip = this.target.invalidText;
10500                 } else if (this.target.regexText.length){
10501                     tip = this.target.regexText;
10502                 }
10503             }
10504
10505             this.toolTip.show(tip);
10506
10507             this.intervalID = window.setInterval(function() {
10508                 Roo.bootstrap.Form.popover.unmask();
10509             }, 10000);
10510
10511             window.onwheel = function(){ return false;};
10512             
10513             (function(){ this.isMasked = true; }).defer(500, this);
10514             
10515         },
10516         
10517         unmask : function()
10518         {
10519             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10520                 return;
10521             }
10522             
10523             this.maskEl.top.setStyle('position', 'absolute');
10524             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10525             this.maskEl.top.hide();
10526
10527             this.maskEl.left.setStyle('position', 'absolute');
10528             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10529             this.maskEl.left.hide();
10530
10531             this.maskEl.bottom.setStyle('position', 'absolute');
10532             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10533             this.maskEl.bottom.hide();
10534
10535             this.maskEl.right.setStyle('position', 'absolute');
10536             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10537             this.maskEl.right.hide();
10538             
10539             this.toolTip.hide();
10540             
10541             this.toolTip.el.hide();
10542             
10543             window.onwheel = function(){ return true;};
10544             
10545             if(this.intervalID){
10546                 window.clearInterval(this.intervalID);
10547                 this.intervalID = false;
10548             }
10549             
10550             this.isMasked = false;
10551             
10552         }
10553         
10554     }
10555     
10556 });
10557
10558 /*
10559  * Based on:
10560  * Ext JS Library 1.1.1
10561  * Copyright(c) 2006-2007, Ext JS, LLC.
10562  *
10563  * Originally Released Under LGPL - original licence link has changed is not relivant.
10564  *
10565  * Fork - LGPL
10566  * <script type="text/javascript">
10567  */
10568 /**
10569  * @class Roo.form.VTypes
10570  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10571  * @singleton
10572  */
10573 Roo.form.VTypes = function(){
10574     // closure these in so they are only created once.
10575     var alpha = /^[a-zA-Z_]+$/;
10576     var alphanum = /^[a-zA-Z0-9_]+$/;
10577     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10578     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10579
10580     // All these messages and functions are configurable
10581     return {
10582         /**
10583          * The function used to validate email addresses
10584          * @param {String} value The email address
10585          */
10586         'email' : function(v){
10587             return email.test(v);
10588         },
10589         /**
10590          * The error text to display when the email validation function returns false
10591          * @type String
10592          */
10593         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10594         /**
10595          * The keystroke filter mask to be applied on email input
10596          * @type RegExp
10597          */
10598         'emailMask' : /[a-z0-9_\.\-@]/i,
10599
10600         /**
10601          * The function used to validate URLs
10602          * @param {String} value The URL
10603          */
10604         'url' : function(v){
10605             return url.test(v);
10606         },
10607         /**
10608          * The error text to display when the url validation function returns false
10609          * @type String
10610          */
10611         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10612         
10613         /**
10614          * The function used to validate alpha values
10615          * @param {String} value The value
10616          */
10617         'alpha' : function(v){
10618             return alpha.test(v);
10619         },
10620         /**
10621          * The error text to display when the alpha validation function returns false
10622          * @type String
10623          */
10624         'alphaText' : 'This field should only contain letters and _',
10625         /**
10626          * The keystroke filter mask to be applied on alpha input
10627          * @type RegExp
10628          */
10629         'alphaMask' : /[a-z_]/i,
10630
10631         /**
10632          * The function used to validate alphanumeric values
10633          * @param {String} value The value
10634          */
10635         'alphanum' : function(v){
10636             return alphanum.test(v);
10637         },
10638         /**
10639          * The error text to display when the alphanumeric validation function returns false
10640          * @type String
10641          */
10642         'alphanumText' : 'This field should only contain letters, numbers and _',
10643         /**
10644          * The keystroke filter mask to be applied on alphanumeric input
10645          * @type RegExp
10646          */
10647         'alphanumMask' : /[a-z0-9_]/i
10648     };
10649 }();/*
10650  * - LGPL
10651  *
10652  * Input
10653  * 
10654  */
10655
10656 /**
10657  * @class Roo.bootstrap.Input
10658  * @extends Roo.bootstrap.Component
10659  * Bootstrap Input class
10660  * @cfg {Boolean} disabled is it disabled
10661  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10662  * @cfg {String} name name of the input
10663  * @cfg {string} fieldLabel - the label associated
10664  * @cfg {string} placeholder - placeholder to put in text.
10665  * @cfg {string}  before - input group add on before
10666  * @cfg {string} after - input group add on after
10667  * @cfg {string} size - (lg|sm) or leave empty..
10668  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10669  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10670  * @cfg {Number} md colspan out of 12 for computer-sized screens
10671  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10672  * @cfg {string} value default value of the input
10673  * @cfg {Number} labelWidth set the width of label 
10674  * @cfg {Number} labellg set the width of label (1-12)
10675  * @cfg {Number} labelmd set the width of label (1-12)
10676  * @cfg {Number} labelsm set the width of label (1-12)
10677  * @cfg {Number} labelxs set the width of label (1-12)
10678  * @cfg {String} labelAlign (top|left)
10679  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10680  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10681  * @cfg {String} indicatorpos (left|right) default left
10682  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10683  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10684  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10685
10686  * @cfg {String} align (left|center|right) Default left
10687  * @cfg {Boolean} forceFeedback (true|false) Default false
10688  * 
10689  * @constructor
10690  * Create a new Input
10691  * @param {Object} config The config object
10692  */
10693
10694 Roo.bootstrap.Input = function(config){
10695     
10696     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10697     
10698     this.addEvents({
10699         /**
10700          * @event focus
10701          * Fires when this field receives input focus.
10702          * @param {Roo.form.Field} this
10703          */
10704         focus : true,
10705         /**
10706          * @event blur
10707          * Fires when this field loses input focus.
10708          * @param {Roo.form.Field} this
10709          */
10710         blur : true,
10711         /**
10712          * @event specialkey
10713          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10714          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10715          * @param {Roo.form.Field} this
10716          * @param {Roo.EventObject} e The event object
10717          */
10718         specialkey : true,
10719         /**
10720          * @event change
10721          * Fires just before the field blurs if the field value has changed.
10722          * @param {Roo.form.Field} this
10723          * @param {Mixed} newValue The new value
10724          * @param {Mixed} oldValue The original value
10725          */
10726         change : true,
10727         /**
10728          * @event invalid
10729          * Fires after the field has been marked as invalid.
10730          * @param {Roo.form.Field} this
10731          * @param {String} msg The validation message
10732          */
10733         invalid : true,
10734         /**
10735          * @event valid
10736          * Fires after the field has been validated with no errors.
10737          * @param {Roo.form.Field} this
10738          */
10739         valid : true,
10740          /**
10741          * @event keyup
10742          * Fires after the key up
10743          * @param {Roo.form.Field} this
10744          * @param {Roo.EventObject}  e The event Object
10745          */
10746         keyup : true,
10747         /**
10748          * @event paste
10749          * Fires after the user pastes into input
10750          * @param {Roo.form.Field} this
10751          * @param {Roo.EventObject}  e The event Object
10752          */
10753         paste : true
10754     });
10755 };
10756
10757 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10758      /**
10759      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10760       automatic validation (defaults to "keyup").
10761      */
10762     validationEvent : "keyup",
10763      /**
10764      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10765      */
10766     validateOnBlur : true,
10767     /**
10768      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10769      */
10770     validationDelay : 250,
10771      /**
10772      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10773      */
10774     focusClass : "x-form-focus",  // not needed???
10775     
10776        
10777     /**
10778      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10779      */
10780     invalidClass : "has-warning",
10781     
10782     /**
10783      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10784      */
10785     validClass : "has-success",
10786     
10787     /**
10788      * @cfg {Boolean} hasFeedback (true|false) default true
10789      */
10790     hasFeedback : true,
10791     
10792     /**
10793      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10794      */
10795     invalidFeedbackClass : "glyphicon-warning-sign",
10796     
10797     /**
10798      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10799      */
10800     validFeedbackClass : "glyphicon-ok",
10801     
10802     /**
10803      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10804      */
10805     selectOnFocus : false,
10806     
10807      /**
10808      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10809      */
10810     maskRe : null,
10811        /**
10812      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10813      */
10814     vtype : null,
10815     
10816       /**
10817      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10818      */
10819     disableKeyFilter : false,
10820     
10821        /**
10822      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10823      */
10824     disabled : false,
10825      /**
10826      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10827      */
10828     allowBlank : true,
10829     /**
10830      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10831      */
10832     blankText : "Please complete this mandatory field",
10833     
10834      /**
10835      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10836      */
10837     minLength : 0,
10838     /**
10839      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10840      */
10841     maxLength : Number.MAX_VALUE,
10842     /**
10843      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10844      */
10845     minLengthText : "The minimum length for this field is {0}",
10846     /**
10847      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10848      */
10849     maxLengthText : "The maximum length for this field is {0}",
10850   
10851     
10852     /**
10853      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10854      * If available, this function will be called only after the basic validators all return true, and will be passed the
10855      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10856      */
10857     validator : null,
10858     /**
10859      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10860      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10861      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10862      */
10863     regex : null,
10864     /**
10865      * @cfg {String} regexText -- Depricated - use Invalid Text
10866      */
10867     regexText : "",
10868     
10869     /**
10870      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10871      */
10872     invalidText : "",
10873     
10874     
10875     
10876     autocomplete: false,
10877     
10878     
10879     fieldLabel : '',
10880     inputType : 'text',
10881     
10882     name : false,
10883     placeholder: false,
10884     before : false,
10885     after : false,
10886     size : false,
10887     hasFocus : false,
10888     preventMark: false,
10889     isFormField : true,
10890     value : '',
10891     labelWidth : 2,
10892     labelAlign : false,
10893     readOnly : false,
10894     align : false,
10895     formatedValue : false,
10896     forceFeedback : false,
10897     
10898     indicatorpos : 'left',
10899     
10900     labellg : 0,
10901     labelmd : 0,
10902     labelsm : 0,
10903     labelxs : 0,
10904     
10905     capture : '',
10906     accept : '',
10907     
10908     parentLabelAlign : function()
10909     {
10910         var parent = this;
10911         while (parent.parent()) {
10912             parent = parent.parent();
10913             if (typeof(parent.labelAlign) !='undefined') {
10914                 return parent.labelAlign;
10915             }
10916         }
10917         return 'left';
10918         
10919     },
10920     
10921     getAutoCreate : function()
10922     {
10923         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10924         
10925         var id = Roo.id();
10926         
10927         var cfg = {};
10928         
10929         if(this.inputType != 'hidden'){
10930             cfg.cls = 'form-group' //input-group
10931         }
10932         
10933         var input =  {
10934             tag: 'input',
10935             id : id,
10936             type : this.inputType,
10937             value : this.value,
10938             cls : 'form-control',
10939             placeholder : this.placeholder || '',
10940             autocomplete : this.autocomplete || 'new-password'
10941         };
10942         if (this.inputType == 'file') {
10943             input.style = 'overflow:hidden'; // why not in CSS?
10944         }
10945         
10946         if(this.capture.length){
10947             input.capture = this.capture;
10948         }
10949         
10950         if(this.accept.length){
10951             input.accept = this.accept + "/*";
10952         }
10953         
10954         if(this.align){
10955             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10956         }
10957         
10958         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10959             input.maxLength = this.maxLength;
10960         }
10961         
10962         if (this.disabled) {
10963             input.disabled=true;
10964         }
10965         
10966         if (this.readOnly) {
10967             input.readonly=true;
10968         }
10969         
10970         if (this.name) {
10971             input.name = this.name;
10972         }
10973         
10974         if (this.size) {
10975             input.cls += ' input-' + this.size;
10976         }
10977         
10978         var settings=this;
10979         ['xs','sm','md','lg'].map(function(size){
10980             if (settings[size]) {
10981                 cfg.cls += ' col-' + size + '-' + settings[size];
10982             }
10983         });
10984         
10985         var inputblock = input;
10986         
10987         var feedback = {
10988             tag: 'span',
10989             cls: 'glyphicon form-control-feedback'
10990         };
10991             
10992         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10993             
10994             inputblock = {
10995                 cls : 'has-feedback',
10996                 cn :  [
10997                     input,
10998                     feedback
10999                 ] 
11000             };  
11001         }
11002         
11003         if (this.before || this.after) {
11004             
11005             inputblock = {
11006                 cls : 'input-group',
11007                 cn :  [] 
11008             };
11009             
11010             if (this.before && typeof(this.before) == 'string') {
11011                 
11012                 inputblock.cn.push({
11013                     tag :'span',
11014                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11015                     html : this.before
11016                 });
11017             }
11018             if (this.before && typeof(this.before) == 'object') {
11019                 this.before = Roo.factory(this.before);
11020                 
11021                 inputblock.cn.push({
11022                     tag :'span',
11023                     cls : 'roo-input-before input-group-prepend   input-group-' +
11024                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11025                 });
11026             }
11027             
11028             inputblock.cn.push(input);
11029             
11030             if (this.after && typeof(this.after) == 'string') {
11031                 inputblock.cn.push({
11032                     tag :'span',
11033                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11034                     html : this.after
11035                 });
11036             }
11037             if (this.after && typeof(this.after) == 'object') {
11038                 this.after = Roo.factory(this.after);
11039                 
11040                 inputblock.cn.push({
11041                     tag :'span',
11042                     cls : 'roo-input-after input-group-append  input-group-' +
11043                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11044                 });
11045             }
11046             
11047             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11048                 inputblock.cls += ' has-feedback';
11049                 inputblock.cn.push(feedback);
11050             }
11051         };
11052         var indicator = {
11053             tag : 'i',
11054             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11055             tooltip : 'This field is required'
11056         };
11057         if (this.allowBlank ) {
11058             indicator.style = this.allowBlank ? ' display:none' : '';
11059         }
11060         if (align ==='left' && this.fieldLabel.length) {
11061             
11062             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11063             
11064             cfg.cn = [
11065                 indicator,
11066                 {
11067                     tag: 'label',
11068                     'for' :  id,
11069                     cls : 'control-label col-form-label',
11070                     html : this.fieldLabel
11071
11072                 },
11073                 {
11074                     cls : "", 
11075                     cn: [
11076                         inputblock
11077                     ]
11078                 }
11079             ];
11080             
11081             var labelCfg = cfg.cn[1];
11082             var contentCfg = cfg.cn[2];
11083             
11084             if(this.indicatorpos == 'right'){
11085                 cfg.cn = [
11086                     {
11087                         tag: 'label',
11088                         'for' :  id,
11089                         cls : 'control-label col-form-label',
11090                         cn : [
11091                             {
11092                                 tag : 'span',
11093                                 html : this.fieldLabel
11094                             },
11095                             indicator
11096                         ]
11097                     },
11098                     {
11099                         cls : "",
11100                         cn: [
11101                             inputblock
11102                         ]
11103                     }
11104
11105                 ];
11106                 
11107                 labelCfg = cfg.cn[0];
11108                 contentCfg = cfg.cn[1];
11109             
11110             }
11111             
11112             if(this.labelWidth > 12){
11113                 labelCfg.style = "width: " + this.labelWidth + 'px';
11114             }
11115             
11116             if(this.labelWidth < 13 && this.labelmd == 0){
11117                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11118             }
11119             
11120             if(this.labellg > 0){
11121                 labelCfg.cls += ' col-lg-' + this.labellg;
11122                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11123             }
11124             
11125             if(this.labelmd > 0){
11126                 labelCfg.cls += ' col-md-' + this.labelmd;
11127                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11128             }
11129             
11130             if(this.labelsm > 0){
11131                 labelCfg.cls += ' col-sm-' + this.labelsm;
11132                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11133             }
11134             
11135             if(this.labelxs > 0){
11136                 labelCfg.cls += ' col-xs-' + this.labelxs;
11137                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11138             }
11139             
11140             
11141         } else if ( this.fieldLabel.length) {
11142                 
11143             
11144             
11145             cfg.cn = [
11146                 {
11147                     tag : 'i',
11148                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11149                     tooltip : 'This field is required',
11150                     style : this.allowBlank ? ' display:none' : '' 
11151                 },
11152                 {
11153                     tag: 'label',
11154                    //cls : 'input-group-addon',
11155                     html : this.fieldLabel
11156
11157                 },
11158
11159                inputblock
11160
11161            ];
11162            
11163            if(this.indicatorpos == 'right'){
11164        
11165                 cfg.cn = [
11166                     {
11167                         tag: 'label',
11168                        //cls : 'input-group-addon',
11169                         html : this.fieldLabel
11170
11171                     },
11172                     {
11173                         tag : 'i',
11174                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11175                         tooltip : 'This field is required',
11176                         style : this.allowBlank ? ' display:none' : '' 
11177                     },
11178
11179                    inputblock
11180
11181                ];
11182
11183             }
11184
11185         } else {
11186             
11187             cfg.cn = [
11188
11189                     inputblock
11190
11191             ];
11192                 
11193                 
11194         };
11195         
11196         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11197            cfg.cls += ' navbar-form';
11198         }
11199         
11200         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11201             // on BS4 we do this only if not form 
11202             cfg.cls += ' navbar-form';
11203             cfg.tag = 'li';
11204         }
11205         
11206         return cfg;
11207         
11208     },
11209     /**
11210      * return the real input element.
11211      */
11212     inputEl: function ()
11213     {
11214         return this.el.select('input.form-control',true).first();
11215     },
11216     
11217     tooltipEl : function()
11218     {
11219         return this.inputEl();
11220     },
11221     
11222     indicatorEl : function()
11223     {
11224         if (Roo.bootstrap.version == 4) {
11225             return false; // not enabled in v4 yet.
11226         }
11227         
11228         var indicator = this.el.select('i.roo-required-indicator',true).first();
11229         
11230         if(!indicator){
11231             return false;
11232         }
11233         
11234         return indicator;
11235         
11236     },
11237     
11238     setDisabled : function(v)
11239     {
11240         var i  = this.inputEl().dom;
11241         if (!v) {
11242             i.removeAttribute('disabled');
11243             return;
11244             
11245         }
11246         i.setAttribute('disabled','true');
11247     },
11248     initEvents : function()
11249     {
11250           
11251         this.inputEl().on("keydown" , this.fireKey,  this);
11252         this.inputEl().on("focus", this.onFocus,  this);
11253         this.inputEl().on("blur", this.onBlur,  this);
11254         
11255         this.inputEl().relayEvent('keyup', this);
11256         this.inputEl().relayEvent('paste', this);
11257         
11258         this.indicator = this.indicatorEl();
11259         
11260         if(this.indicator){
11261             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11262         }
11263  
11264         // reference to original value for reset
11265         this.originalValue = this.getValue();
11266         //Roo.form.TextField.superclass.initEvents.call(this);
11267         if(this.validationEvent == 'keyup'){
11268             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11269             this.inputEl().on('keyup', this.filterValidation, this);
11270         }
11271         else if(this.validationEvent !== false){
11272             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11273         }
11274         
11275         if(this.selectOnFocus){
11276             this.on("focus", this.preFocus, this);
11277             
11278         }
11279         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11280             this.inputEl().on("keypress", this.filterKeys, this);
11281         } else {
11282             this.inputEl().relayEvent('keypress', this);
11283         }
11284        /* if(this.grow){
11285             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11286             this.el.on("click", this.autoSize,  this);
11287         }
11288         */
11289         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11290             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11291         }
11292         
11293         if (typeof(this.before) == 'object') {
11294             this.before.render(this.el.select('.roo-input-before',true).first());
11295         }
11296         if (typeof(this.after) == 'object') {
11297             this.after.render(this.el.select('.roo-input-after',true).first());
11298         }
11299         
11300         this.inputEl().on('change', this.onChange, this);
11301         
11302     },
11303     filterValidation : function(e){
11304         if(!e.isNavKeyPress()){
11305             this.validationTask.delay(this.validationDelay);
11306         }
11307     },
11308      /**
11309      * Validates the field value
11310      * @return {Boolean} True if the value is valid, else false
11311      */
11312     validate : function(){
11313         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11314         if(this.disabled || this.validateValue(this.getRawValue())){
11315             this.markValid();
11316             return true;
11317         }
11318         
11319         this.markInvalid();
11320         return false;
11321     },
11322     
11323     
11324     /**
11325      * Validates a value according to the field's validation rules and marks the field as invalid
11326      * if the validation fails
11327      * @param {Mixed} value The value to validate
11328      * @return {Boolean} True if the value is valid, else false
11329      */
11330     validateValue : function(value)
11331     {
11332         if(this.getVisibilityEl().hasClass('hidden')){
11333             return true;
11334         }
11335         
11336         if(value.length < 1)  { // if it's blank
11337             if(this.allowBlank){
11338                 return true;
11339             }
11340             return false;
11341         }
11342         
11343         if(value.length < this.minLength){
11344             return false;
11345         }
11346         if(value.length > this.maxLength){
11347             return false;
11348         }
11349         if(this.vtype){
11350             var vt = Roo.form.VTypes;
11351             if(!vt[this.vtype](value, this)){
11352                 return false;
11353             }
11354         }
11355         if(typeof this.validator == "function"){
11356             var msg = this.validator(value);
11357             if(msg !== true){
11358                 return false;
11359             }
11360             if (typeof(msg) == 'string') {
11361                 this.invalidText = msg;
11362             }
11363         }
11364         
11365         if(this.regex && !this.regex.test(value)){
11366             return false;
11367         }
11368         
11369         return true;
11370     },
11371     
11372      // private
11373     fireKey : function(e){
11374         //Roo.log('field ' + e.getKey());
11375         if(e.isNavKeyPress()){
11376             this.fireEvent("specialkey", this, e);
11377         }
11378     },
11379     focus : function (selectText){
11380         if(this.rendered){
11381             this.inputEl().focus();
11382             if(selectText === true){
11383                 this.inputEl().dom.select();
11384             }
11385         }
11386         return this;
11387     } ,
11388     
11389     onFocus : function(){
11390         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11391            // this.el.addClass(this.focusClass);
11392         }
11393         if(!this.hasFocus){
11394             this.hasFocus = true;
11395             this.startValue = this.getValue();
11396             this.fireEvent("focus", this);
11397         }
11398     },
11399     
11400     beforeBlur : Roo.emptyFn,
11401
11402     
11403     // private
11404     onBlur : function(){
11405         this.beforeBlur();
11406         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11407             //this.el.removeClass(this.focusClass);
11408         }
11409         this.hasFocus = false;
11410         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11411             this.validate();
11412         }
11413         var v = this.getValue();
11414         if(String(v) !== String(this.startValue)){
11415             this.fireEvent('change', this, v, this.startValue);
11416         }
11417         this.fireEvent("blur", this);
11418     },
11419     
11420     onChange : function(e)
11421     {
11422         var v = this.getValue();
11423         if(String(v) !== String(this.startValue)){
11424             this.fireEvent('change', this, v, this.startValue);
11425         }
11426         
11427     },
11428     
11429     /**
11430      * Resets the current field value to the originally loaded value and clears any validation messages
11431      */
11432     reset : function(){
11433         this.setValue(this.originalValue);
11434         this.validate();
11435     },
11436      /**
11437      * Returns the name of the field
11438      * @return {Mixed} name The name field
11439      */
11440     getName: function(){
11441         return this.name;
11442     },
11443      /**
11444      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11445      * @return {Mixed} value The field value
11446      */
11447     getValue : function(){
11448         
11449         var v = this.inputEl().getValue();
11450         
11451         return v;
11452     },
11453     /**
11454      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11455      * @return {Mixed} value The field value
11456      */
11457     getRawValue : function(){
11458         var v = this.inputEl().getValue();
11459         
11460         return v;
11461     },
11462     
11463     /**
11464      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11465      * @param {Mixed} value The value to set
11466      */
11467     setRawValue : function(v){
11468         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11469     },
11470     
11471     selectText : function(start, end){
11472         var v = this.getRawValue();
11473         if(v.length > 0){
11474             start = start === undefined ? 0 : start;
11475             end = end === undefined ? v.length : end;
11476             var d = this.inputEl().dom;
11477             if(d.setSelectionRange){
11478                 d.setSelectionRange(start, end);
11479             }else if(d.createTextRange){
11480                 var range = d.createTextRange();
11481                 range.moveStart("character", start);
11482                 range.moveEnd("character", v.length-end);
11483                 range.select();
11484             }
11485         }
11486     },
11487     
11488     /**
11489      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11490      * @param {Mixed} value The value to set
11491      */
11492     setValue : function(v){
11493         this.value = v;
11494         if(this.rendered){
11495             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11496             this.validate();
11497         }
11498     },
11499     
11500     /*
11501     processValue : function(value){
11502         if(this.stripCharsRe){
11503             var newValue = value.replace(this.stripCharsRe, '');
11504             if(newValue !== value){
11505                 this.setRawValue(newValue);
11506                 return newValue;
11507             }
11508         }
11509         return value;
11510     },
11511   */
11512     preFocus : function(){
11513         
11514         if(this.selectOnFocus){
11515             this.inputEl().dom.select();
11516         }
11517     },
11518     filterKeys : function(e){
11519         var k = e.getKey();
11520         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11521             return;
11522         }
11523         var c = e.getCharCode(), cc = String.fromCharCode(c);
11524         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11525             return;
11526         }
11527         if(!this.maskRe.test(cc)){
11528             e.stopEvent();
11529         }
11530     },
11531      /**
11532      * Clear any invalid styles/messages for this field
11533      */
11534     clearInvalid : function(){
11535         
11536         if(!this.el || this.preventMark){ // not rendered
11537             return;
11538         }
11539         
11540         
11541         this.el.removeClass([this.invalidClass, 'is-invalid']);
11542         
11543         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11544             
11545             var feedback = this.el.select('.form-control-feedback', true).first();
11546             
11547             if(feedback){
11548                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11549             }
11550             
11551         }
11552         
11553         if(this.indicator){
11554             this.indicator.removeClass('visible');
11555             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11556         }
11557         
11558         this.fireEvent('valid', this);
11559     },
11560     
11561      /**
11562      * Mark this field as valid
11563      */
11564     markValid : function()
11565     {
11566         if(!this.el  || this.preventMark){ // not rendered...
11567             return;
11568         }
11569         
11570         this.el.removeClass([this.invalidClass, this.validClass]);
11571         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11572
11573         var feedback = this.el.select('.form-control-feedback', true).first();
11574             
11575         if(feedback){
11576             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11577         }
11578         
11579         if(this.indicator){
11580             this.indicator.removeClass('visible');
11581             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11582         }
11583         
11584         if(this.disabled){
11585             return;
11586         }
11587         
11588            
11589         if(this.allowBlank && !this.getRawValue().length){
11590             return;
11591         }
11592         if (Roo.bootstrap.version == 3) {
11593             this.el.addClass(this.validClass);
11594         } else {
11595             this.inputEl().addClass('is-valid');
11596         }
11597
11598         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11599             
11600             var feedback = this.el.select('.form-control-feedback', true).first();
11601             
11602             if(feedback){
11603                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11604                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11605             }
11606             
11607         }
11608         
11609         this.fireEvent('valid', this);
11610     },
11611     
11612      /**
11613      * Mark this field as invalid
11614      * @param {String} msg The validation message
11615      */
11616     markInvalid : function(msg)
11617     {
11618         if(!this.el  || this.preventMark){ // not rendered
11619             return;
11620         }
11621         
11622         this.el.removeClass([this.invalidClass, this.validClass]);
11623         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11624         
11625         var feedback = this.el.select('.form-control-feedback', true).first();
11626             
11627         if(feedback){
11628             this.el.select('.form-control-feedback', true).first().removeClass(
11629                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11630         }
11631
11632         if(this.disabled){
11633             return;
11634         }
11635         
11636         if(this.allowBlank && !this.getRawValue().length){
11637             return;
11638         }
11639         
11640         if(this.indicator){
11641             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11642             this.indicator.addClass('visible');
11643         }
11644         if (Roo.bootstrap.version == 3) {
11645             this.el.addClass(this.invalidClass);
11646         } else {
11647             this.inputEl().addClass('is-invalid');
11648         }
11649         
11650         
11651         
11652         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11653             
11654             var feedback = this.el.select('.form-control-feedback', true).first();
11655             
11656             if(feedback){
11657                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11658                 
11659                 if(this.getValue().length || this.forceFeedback){
11660                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11661                 }
11662                 
11663             }
11664             
11665         }
11666         
11667         this.fireEvent('invalid', this, msg);
11668     },
11669     // private
11670     SafariOnKeyDown : function(event)
11671     {
11672         // this is a workaround for a password hang bug on chrome/ webkit.
11673         if (this.inputEl().dom.type != 'password') {
11674             return;
11675         }
11676         
11677         var isSelectAll = false;
11678         
11679         if(this.inputEl().dom.selectionEnd > 0){
11680             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11681         }
11682         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11683             event.preventDefault();
11684             this.setValue('');
11685             return;
11686         }
11687         
11688         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11689             
11690             event.preventDefault();
11691             // this is very hacky as keydown always get's upper case.
11692             //
11693             var cc = String.fromCharCode(event.getCharCode());
11694             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11695             
11696         }
11697     },
11698     adjustWidth : function(tag, w){
11699         tag = tag.toLowerCase();
11700         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11701             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11702                 if(tag == 'input'){
11703                     return w + 2;
11704                 }
11705                 if(tag == 'textarea'){
11706                     return w-2;
11707                 }
11708             }else if(Roo.isOpera){
11709                 if(tag == 'input'){
11710                     return w + 2;
11711                 }
11712                 if(tag == 'textarea'){
11713                     return w-2;
11714                 }
11715             }
11716         }
11717         return w;
11718     },
11719     
11720     setFieldLabel : function(v)
11721     {
11722         if(!this.rendered){
11723             return;
11724         }
11725         
11726         if(this.indicatorEl()){
11727             var ar = this.el.select('label > span',true);
11728             
11729             if (ar.elements.length) {
11730                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11731                 this.fieldLabel = v;
11732                 return;
11733             }
11734             
11735             var br = this.el.select('label',true);
11736             
11737             if(br.elements.length) {
11738                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11739                 this.fieldLabel = v;
11740                 return;
11741             }
11742             
11743             Roo.log('Cannot Found any of label > span || label in input');
11744             return;
11745         }
11746         
11747         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11748         this.fieldLabel = v;
11749         
11750         
11751     }
11752 });
11753
11754  
11755 /*
11756  * - LGPL
11757  *
11758  * Input
11759  * 
11760  */
11761
11762 /**
11763  * @class Roo.bootstrap.TextArea
11764  * @extends Roo.bootstrap.Input
11765  * Bootstrap TextArea class
11766  * @cfg {Number} cols Specifies the visible width of a text area
11767  * @cfg {Number} rows Specifies the visible number of lines in a text area
11768  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11769  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11770  * @cfg {string} html text
11771  * 
11772  * @constructor
11773  * Create a new TextArea
11774  * @param {Object} config The config object
11775  */
11776
11777 Roo.bootstrap.TextArea = function(config){
11778     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11779    
11780 };
11781
11782 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11783      
11784     cols : false,
11785     rows : 5,
11786     readOnly : false,
11787     warp : 'soft',
11788     resize : false,
11789     value: false,
11790     html: false,
11791     
11792     getAutoCreate : function(){
11793         
11794         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11795         
11796         var id = Roo.id();
11797         
11798         var cfg = {};
11799         
11800         if(this.inputType != 'hidden'){
11801             cfg.cls = 'form-group' //input-group
11802         }
11803         
11804         var input =  {
11805             tag: 'textarea',
11806             id : id,
11807             warp : this.warp,
11808             rows : this.rows,
11809             value : this.value || '',
11810             html: this.html || '',
11811             cls : 'form-control',
11812             placeholder : this.placeholder || '' 
11813             
11814         };
11815         
11816         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11817             input.maxLength = this.maxLength;
11818         }
11819         
11820         if(this.resize){
11821             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11822         }
11823         
11824         if(this.cols){
11825             input.cols = this.cols;
11826         }
11827         
11828         if (this.readOnly) {
11829             input.readonly = true;
11830         }
11831         
11832         if (this.name) {
11833             input.name = this.name;
11834         }
11835         
11836         if (this.size) {
11837             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11838         }
11839         
11840         var settings=this;
11841         ['xs','sm','md','lg'].map(function(size){
11842             if (settings[size]) {
11843                 cfg.cls += ' col-' + size + '-' + settings[size];
11844             }
11845         });
11846         
11847         var inputblock = input;
11848         
11849         if(this.hasFeedback && !this.allowBlank){
11850             
11851             var feedback = {
11852                 tag: 'span',
11853                 cls: 'glyphicon form-control-feedback'
11854             };
11855
11856             inputblock = {
11857                 cls : 'has-feedback',
11858                 cn :  [
11859                     input,
11860                     feedback
11861                 ] 
11862             };  
11863         }
11864         
11865         
11866         if (this.before || this.after) {
11867             
11868             inputblock = {
11869                 cls : 'input-group',
11870                 cn :  [] 
11871             };
11872             if (this.before) {
11873                 inputblock.cn.push({
11874                     tag :'span',
11875                     cls : 'input-group-addon',
11876                     html : this.before
11877                 });
11878             }
11879             
11880             inputblock.cn.push(input);
11881             
11882             if(this.hasFeedback && !this.allowBlank){
11883                 inputblock.cls += ' has-feedback';
11884                 inputblock.cn.push(feedback);
11885             }
11886             
11887             if (this.after) {
11888                 inputblock.cn.push({
11889                     tag :'span',
11890                     cls : 'input-group-addon',
11891                     html : this.after
11892                 });
11893             }
11894             
11895         }
11896         
11897         if (align ==='left' && this.fieldLabel.length) {
11898             cfg.cn = [
11899                 {
11900                     tag: 'label',
11901                     'for' :  id,
11902                     cls : 'control-label',
11903                     html : this.fieldLabel
11904                 },
11905                 {
11906                     cls : "",
11907                     cn: [
11908                         inputblock
11909                     ]
11910                 }
11911
11912             ];
11913             
11914             if(this.labelWidth > 12){
11915                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11916             }
11917
11918             if(this.labelWidth < 13 && this.labelmd == 0){
11919                 this.labelmd = this.labelWidth;
11920             }
11921
11922             if(this.labellg > 0){
11923                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11924                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11925             }
11926
11927             if(this.labelmd > 0){
11928                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11929                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11930             }
11931
11932             if(this.labelsm > 0){
11933                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11934                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11935             }
11936
11937             if(this.labelxs > 0){
11938                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11939                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11940             }
11941             
11942         } else if ( this.fieldLabel.length) {
11943             cfg.cn = [
11944
11945                {
11946                    tag: 'label',
11947                    //cls : 'input-group-addon',
11948                    html : this.fieldLabel
11949
11950                },
11951
11952                inputblock
11953
11954            ];
11955
11956         } else {
11957
11958             cfg.cn = [
11959
11960                 inputblock
11961
11962             ];
11963                 
11964         }
11965         
11966         if (this.disabled) {
11967             input.disabled=true;
11968         }
11969         
11970         return cfg;
11971         
11972     },
11973     /**
11974      * return the real textarea element.
11975      */
11976     inputEl: function ()
11977     {
11978         return this.el.select('textarea.form-control',true).first();
11979     },
11980     
11981     /**
11982      * Clear any invalid styles/messages for this field
11983      */
11984     clearInvalid : function()
11985     {
11986         
11987         if(!this.el || this.preventMark){ // not rendered
11988             return;
11989         }
11990         
11991         var label = this.el.select('label', true).first();
11992         var icon = this.el.select('i.fa-star', true).first();
11993         
11994         if(label && icon){
11995             icon.remove();
11996         }
11997         this.el.removeClass( this.validClass);
11998         this.inputEl().removeClass('is-invalid');
11999          
12000         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12001             
12002             var feedback = this.el.select('.form-control-feedback', true).first();
12003             
12004             if(feedback){
12005                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12006             }
12007             
12008         }
12009         
12010         this.fireEvent('valid', this);
12011     },
12012     
12013      /**
12014      * Mark this field as valid
12015      */
12016     markValid : function()
12017     {
12018         if(!this.el  || this.preventMark){ // not rendered
12019             return;
12020         }
12021         
12022         this.el.removeClass([this.invalidClass, this.validClass]);
12023         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12024         
12025         var feedback = this.el.select('.form-control-feedback', true).first();
12026             
12027         if(feedback){
12028             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12029         }
12030
12031         if(this.disabled || this.allowBlank){
12032             return;
12033         }
12034         
12035         var label = this.el.select('label', true).first();
12036         var icon = this.el.select('i.fa-star', true).first();
12037         
12038         if(label && icon){
12039             icon.remove();
12040         }
12041         if (Roo.bootstrap.version == 3) {
12042             this.el.addClass(this.validClass);
12043         } else {
12044             this.inputEl().addClass('is-valid');
12045         }
12046         
12047         
12048         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12049             
12050             var feedback = this.el.select('.form-control-feedback', true).first();
12051             
12052             if(feedback){
12053                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12054                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12055             }
12056             
12057         }
12058         
12059         this.fireEvent('valid', this);
12060     },
12061     
12062      /**
12063      * Mark this field as invalid
12064      * @param {String} msg The validation message
12065      */
12066     markInvalid : function(msg)
12067     {
12068         if(!this.el  || this.preventMark){ // not rendered
12069             return;
12070         }
12071         
12072         this.el.removeClass([this.invalidClass, this.validClass]);
12073         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12074         
12075         var feedback = this.el.select('.form-control-feedback', true).first();
12076             
12077         if(feedback){
12078             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12079         }
12080
12081         if(this.disabled || this.allowBlank){
12082             return;
12083         }
12084         
12085         var label = this.el.select('label', true).first();
12086         var icon = this.el.select('i.fa-star', true).first();
12087         
12088         if(!this.getValue().length && label && !icon){
12089             this.el.createChild({
12090                 tag : 'i',
12091                 cls : 'text-danger fa fa-lg fa-star',
12092                 tooltip : 'This field is required',
12093                 style : 'margin-right:5px;'
12094             }, label, true);
12095         }
12096         
12097         if (Roo.bootstrap.version == 3) {
12098             this.el.addClass(this.invalidClass);
12099         } else {
12100             this.inputEl().addClass('is-invalid');
12101         }
12102         
12103         // fixme ... this may be depricated need to test..
12104         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12105             
12106             var feedback = this.el.select('.form-control-feedback', true).first();
12107             
12108             if(feedback){
12109                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12110                 
12111                 if(this.getValue().length || this.forceFeedback){
12112                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12113                 }
12114                 
12115             }
12116             
12117         }
12118         
12119         this.fireEvent('invalid', this, msg);
12120     }
12121 });
12122
12123  
12124 /*
12125  * - LGPL
12126  *
12127  * trigger field - base class for combo..
12128  * 
12129  */
12130  
12131 /**
12132  * @class Roo.bootstrap.TriggerField
12133  * @extends Roo.bootstrap.Input
12134  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12135  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12136  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12137  * for which you can provide a custom implementation.  For example:
12138  * <pre><code>
12139 var trigger = new Roo.bootstrap.TriggerField();
12140 trigger.onTriggerClick = myTriggerFn;
12141 trigger.applyTo('my-field');
12142 </code></pre>
12143  *
12144  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12145  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12146  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12147  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12148  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12149
12150  * @constructor
12151  * Create a new TriggerField.
12152  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12153  * to the base TextField)
12154  */
12155 Roo.bootstrap.TriggerField = function(config){
12156     this.mimicing = false;
12157     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12158 };
12159
12160 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12161     /**
12162      * @cfg {String} triggerClass A CSS class to apply to the trigger
12163      */
12164      /**
12165      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12166      */
12167     hideTrigger:false,
12168
12169     /**
12170      * @cfg {Boolean} removable (true|false) special filter default false
12171      */
12172     removable : false,
12173     
12174     /** @cfg {Boolean} grow @hide */
12175     /** @cfg {Number} growMin @hide */
12176     /** @cfg {Number} growMax @hide */
12177
12178     /**
12179      * @hide 
12180      * @method
12181      */
12182     autoSize: Roo.emptyFn,
12183     // private
12184     monitorTab : true,
12185     // private
12186     deferHeight : true,
12187
12188     
12189     actionMode : 'wrap',
12190     
12191     caret : false,
12192     
12193     
12194     getAutoCreate : function(){
12195        
12196         var align = this.labelAlign || this.parentLabelAlign();
12197         
12198         var id = Roo.id();
12199         
12200         var cfg = {
12201             cls: 'form-group' //input-group
12202         };
12203         
12204         
12205         var input =  {
12206             tag: 'input',
12207             id : id,
12208             type : this.inputType,
12209             cls : 'form-control',
12210             autocomplete: 'new-password',
12211             placeholder : this.placeholder || '' 
12212             
12213         };
12214         if (this.name) {
12215             input.name = this.name;
12216         }
12217         if (this.size) {
12218             input.cls += ' input-' + this.size;
12219         }
12220         
12221         if (this.disabled) {
12222             input.disabled=true;
12223         }
12224         
12225         var inputblock = input;
12226         
12227         if(this.hasFeedback && !this.allowBlank){
12228             
12229             var feedback = {
12230                 tag: 'span',
12231                 cls: 'glyphicon form-control-feedback'
12232             };
12233             
12234             if(this.removable && !this.editable  ){
12235                 inputblock = {
12236                     cls : 'has-feedback',
12237                     cn :  [
12238                         inputblock,
12239                         {
12240                             tag: 'button',
12241                             html : 'x',
12242                             cls : 'roo-combo-removable-btn close'
12243                         },
12244                         feedback
12245                     ] 
12246                 };
12247             } else {
12248                 inputblock = {
12249                     cls : 'has-feedback',
12250                     cn :  [
12251                         inputblock,
12252                         feedback
12253                     ] 
12254                 };
12255             }
12256
12257         } else {
12258             if(this.removable && !this.editable ){
12259                 inputblock = {
12260                     cls : 'roo-removable',
12261                     cn :  [
12262                         inputblock,
12263                         {
12264                             tag: 'button',
12265                             html : 'x',
12266                             cls : 'roo-combo-removable-btn close'
12267                         }
12268                     ] 
12269                 };
12270             }
12271         }
12272         
12273         if (this.before || this.after) {
12274             
12275             inputblock = {
12276                 cls : 'input-group',
12277                 cn :  [] 
12278             };
12279             if (this.before) {
12280                 inputblock.cn.push({
12281                     tag :'span',
12282                     cls : 'input-group-addon input-group-prepend input-group-text',
12283                     html : this.before
12284                 });
12285             }
12286             
12287             inputblock.cn.push(input);
12288             
12289             if(this.hasFeedback && !this.allowBlank){
12290                 inputblock.cls += ' has-feedback';
12291                 inputblock.cn.push(feedback);
12292             }
12293             
12294             if (this.after) {
12295                 inputblock.cn.push({
12296                     tag :'span',
12297                     cls : 'input-group-addon input-group-append input-group-text',
12298                     html : this.after
12299                 });
12300             }
12301             
12302         };
12303         
12304       
12305         
12306         var ibwrap = inputblock;
12307         
12308         if(this.multiple){
12309             ibwrap = {
12310                 tag: 'ul',
12311                 cls: 'roo-select2-choices',
12312                 cn:[
12313                     {
12314                         tag: 'li',
12315                         cls: 'roo-select2-search-field',
12316                         cn: [
12317
12318                             inputblock
12319                         ]
12320                     }
12321                 ]
12322             };
12323                 
12324         }
12325         
12326         var combobox = {
12327             cls: 'roo-select2-container input-group',
12328             cn: [
12329                  {
12330                     tag: 'input',
12331                     type : 'hidden',
12332                     cls: 'form-hidden-field'
12333                 },
12334                 ibwrap
12335             ]
12336         };
12337         
12338         if(!this.multiple && this.showToggleBtn){
12339             
12340             var caret = {
12341                         tag: 'span',
12342                         cls: 'caret'
12343              };
12344             if (this.caret != false) {
12345                 caret = {
12346                      tag: 'i',
12347                      cls: 'fa fa-' + this.caret
12348                 };
12349                 
12350             }
12351             
12352             combobox.cn.push({
12353                 tag :'span',
12354                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12355                 cn : [
12356                     Roo.bootstrap.version == 3 ? caret : '',
12357                     {
12358                         tag: 'span',
12359                         cls: 'combobox-clear',
12360                         cn  : [
12361                             {
12362                                 tag : 'i',
12363                                 cls: 'icon-remove'
12364                             }
12365                         ]
12366                     }
12367                 ]
12368
12369             })
12370         }
12371         
12372         if(this.multiple){
12373             combobox.cls += ' roo-select2-container-multi';
12374         }
12375          var indicator = {
12376             tag : 'i',
12377             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12378             tooltip : 'This field is required'
12379         };
12380         if (Roo.bootstrap.version == 4) {
12381             indicator = {
12382                 tag : 'i',
12383                 style : 'display:none'
12384             };
12385         }
12386         
12387         
12388         if (align ==='left' && this.fieldLabel.length) {
12389             
12390             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12391
12392             cfg.cn = [
12393                 indicator,
12394                 {
12395                     tag: 'label',
12396                     'for' :  id,
12397                     cls : 'control-label',
12398                     html : this.fieldLabel
12399
12400                 },
12401                 {
12402                     cls : "", 
12403                     cn: [
12404                         combobox
12405                     ]
12406                 }
12407
12408             ];
12409             
12410             var labelCfg = cfg.cn[1];
12411             var contentCfg = cfg.cn[2];
12412             
12413             if(this.indicatorpos == 'right'){
12414                 cfg.cn = [
12415                     {
12416                         tag: 'label',
12417                         'for' :  id,
12418                         cls : 'control-label',
12419                         cn : [
12420                             {
12421                                 tag : 'span',
12422                                 html : this.fieldLabel
12423                             },
12424                             indicator
12425                         ]
12426                     },
12427                     {
12428                         cls : "", 
12429                         cn: [
12430                             combobox
12431                         ]
12432                     }
12433
12434                 ];
12435                 
12436                 labelCfg = cfg.cn[0];
12437                 contentCfg = cfg.cn[1];
12438             }
12439             
12440             if(this.labelWidth > 12){
12441                 labelCfg.style = "width: " + this.labelWidth + 'px';
12442             }
12443             
12444             if(this.labelWidth < 13 && this.labelmd == 0){
12445                 this.labelmd = this.labelWidth;
12446             }
12447             
12448             if(this.labellg > 0){
12449                 labelCfg.cls += ' col-lg-' + this.labellg;
12450                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12451             }
12452             
12453             if(this.labelmd > 0){
12454                 labelCfg.cls += ' col-md-' + this.labelmd;
12455                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12456             }
12457             
12458             if(this.labelsm > 0){
12459                 labelCfg.cls += ' col-sm-' + this.labelsm;
12460                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12461             }
12462             
12463             if(this.labelxs > 0){
12464                 labelCfg.cls += ' col-xs-' + this.labelxs;
12465                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12466             }
12467             
12468         } else if ( this.fieldLabel.length) {
12469 //                Roo.log(" label");
12470             cfg.cn = [
12471                 indicator,
12472                {
12473                    tag: 'label',
12474                    //cls : 'input-group-addon',
12475                    html : this.fieldLabel
12476
12477                },
12478
12479                combobox
12480
12481             ];
12482             
12483             if(this.indicatorpos == 'right'){
12484                 
12485                 cfg.cn = [
12486                     {
12487                        tag: 'label',
12488                        cn : [
12489                            {
12490                                tag : 'span',
12491                                html : this.fieldLabel
12492                            },
12493                            indicator
12494                        ]
12495
12496                     },
12497                     combobox
12498
12499                 ];
12500
12501             }
12502
12503         } else {
12504             
12505 //                Roo.log(" no label && no align");
12506                 cfg = combobox
12507                      
12508                 
12509         }
12510         
12511         var settings=this;
12512         ['xs','sm','md','lg'].map(function(size){
12513             if (settings[size]) {
12514                 cfg.cls += ' col-' + size + '-' + settings[size];
12515             }
12516         });
12517         
12518         return cfg;
12519         
12520     },
12521     
12522     
12523     
12524     // private
12525     onResize : function(w, h){
12526 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12527 //        if(typeof w == 'number'){
12528 //            var x = w - this.trigger.getWidth();
12529 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12530 //            this.trigger.setStyle('left', x+'px');
12531 //        }
12532     },
12533
12534     // private
12535     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12536
12537     // private
12538     getResizeEl : function(){
12539         return this.inputEl();
12540     },
12541
12542     // private
12543     getPositionEl : function(){
12544         return this.inputEl();
12545     },
12546
12547     // private
12548     alignErrorIcon : function(){
12549         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12550     },
12551
12552     // private
12553     initEvents : function(){
12554         
12555         this.createList();
12556         
12557         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12558         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12559         if(!this.multiple && this.showToggleBtn){
12560             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12561             if(this.hideTrigger){
12562                 this.trigger.setDisplayed(false);
12563             }
12564             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12565         }
12566         
12567         if(this.multiple){
12568             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12569         }
12570         
12571         if(this.removable && !this.editable && !this.tickable){
12572             var close = this.closeTriggerEl();
12573             
12574             if(close){
12575                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12576                 close.on('click', this.removeBtnClick, this, close);
12577             }
12578         }
12579         
12580         //this.trigger.addClassOnOver('x-form-trigger-over');
12581         //this.trigger.addClassOnClick('x-form-trigger-click');
12582         
12583         //if(!this.width){
12584         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12585         //}
12586     },
12587     
12588     closeTriggerEl : function()
12589     {
12590         var close = this.el.select('.roo-combo-removable-btn', true).first();
12591         return close ? close : false;
12592     },
12593     
12594     removeBtnClick : function(e, h, el)
12595     {
12596         e.preventDefault();
12597         
12598         if(this.fireEvent("remove", this) !== false){
12599             this.reset();
12600             this.fireEvent("afterremove", this)
12601         }
12602     },
12603     
12604     createList : function()
12605     {
12606         this.list = Roo.get(document.body).createChild({
12607             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12608             cls: 'typeahead typeahead-long dropdown-menu shadow',
12609             style: 'display:none'
12610         });
12611         
12612         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12613         
12614     },
12615
12616     // private
12617     initTrigger : function(){
12618        
12619     },
12620
12621     // private
12622     onDestroy : function(){
12623         if(this.trigger){
12624             this.trigger.removeAllListeners();
12625           //  this.trigger.remove();
12626         }
12627         //if(this.wrap){
12628         //    this.wrap.remove();
12629         //}
12630         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12631     },
12632
12633     // private
12634     onFocus : function(){
12635         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12636         /*
12637         if(!this.mimicing){
12638             this.wrap.addClass('x-trigger-wrap-focus');
12639             this.mimicing = true;
12640             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12641             if(this.monitorTab){
12642                 this.el.on("keydown", this.checkTab, this);
12643             }
12644         }
12645         */
12646     },
12647
12648     // private
12649     checkTab : function(e){
12650         if(e.getKey() == e.TAB){
12651             this.triggerBlur();
12652         }
12653     },
12654
12655     // private
12656     onBlur : function(){
12657         // do nothing
12658     },
12659
12660     // private
12661     mimicBlur : function(e, t){
12662         /*
12663         if(!this.wrap.contains(t) && this.validateBlur()){
12664             this.triggerBlur();
12665         }
12666         */
12667     },
12668
12669     // private
12670     triggerBlur : function(){
12671         this.mimicing = false;
12672         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12673         if(this.monitorTab){
12674             this.el.un("keydown", this.checkTab, this);
12675         }
12676         //this.wrap.removeClass('x-trigger-wrap-focus');
12677         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12678     },
12679
12680     // private
12681     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12682     validateBlur : function(e, t){
12683         return true;
12684     },
12685
12686     // private
12687     onDisable : function(){
12688         this.inputEl().dom.disabled = true;
12689         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12690         //if(this.wrap){
12691         //    this.wrap.addClass('x-item-disabled');
12692         //}
12693     },
12694
12695     // private
12696     onEnable : function(){
12697         this.inputEl().dom.disabled = false;
12698         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12699         //if(this.wrap){
12700         //    this.el.removeClass('x-item-disabled');
12701         //}
12702     },
12703
12704     // private
12705     onShow : function(){
12706         var ae = this.getActionEl();
12707         
12708         if(ae){
12709             ae.dom.style.display = '';
12710             ae.dom.style.visibility = 'visible';
12711         }
12712     },
12713
12714     // private
12715     
12716     onHide : function(){
12717         var ae = this.getActionEl();
12718         ae.dom.style.display = 'none';
12719     },
12720
12721     /**
12722      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12723      * by an implementing function.
12724      * @method
12725      * @param {EventObject} e
12726      */
12727     onTriggerClick : Roo.emptyFn
12728 });
12729  
12730 /*
12731 * Licence: LGPL
12732 */
12733
12734 /**
12735  * @class Roo.bootstrap.CardUploader
12736  * @extends Roo.bootstrap.Button
12737  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12738  * @cfg {Number} errorTimeout default 3000
12739  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12740  * @cfg {Array}  html The button text.
12741
12742  *
12743  * @constructor
12744  * Create a new CardUploader
12745  * @param {Object} config The config object
12746  */
12747
12748 Roo.bootstrap.CardUploader = function(config){
12749     
12750  
12751     
12752     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12753     
12754     
12755     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12756         return r.data.id
12757      });
12758     
12759      this.addEvents({
12760          // raw events
12761         /**
12762          * @event preview
12763          * When a image is clicked on - and needs to display a slideshow or similar..
12764          * @param {Roo.bootstrap.Card} this
12765          * @param {Object} The image information data 
12766          *
12767          */
12768         'preview' : true,
12769          /**
12770          * @event download
12771          * When a the download link is clicked
12772          * @param {Roo.bootstrap.Card} this
12773          * @param {Object} The image information data  contains 
12774          */
12775         'download' : true
12776         
12777     });
12778 };
12779  
12780 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12781     
12782      
12783     errorTimeout : 3000,
12784      
12785     images : false,
12786    
12787     fileCollection : false,
12788     allowBlank : true,
12789     
12790     getAutoCreate : function()
12791     {
12792         
12793         var cfg =  {
12794             cls :'form-group' ,
12795             cn : [
12796                
12797                 {
12798                     tag: 'label',
12799                    //cls : 'input-group-addon',
12800                     html : this.fieldLabel
12801
12802                 },
12803
12804                 {
12805                     tag: 'input',
12806                     type : 'hidden',
12807                     name : this.name,
12808                     value : this.value,
12809                     cls : 'd-none  form-control'
12810                 },
12811                 
12812                 {
12813                     tag: 'input',
12814                     multiple : 'multiple',
12815                     type : 'file',
12816                     cls : 'd-none  roo-card-upload-selector'
12817                 },
12818                 
12819                 {
12820                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12821                 },
12822                 {
12823                     cls : 'card-columns roo-card-uploader-container'
12824                 }
12825
12826             ]
12827         };
12828            
12829          
12830         return cfg;
12831     },
12832     
12833     getChildContainer : function() /// what children are added to.
12834     {
12835         return this.containerEl;
12836     },
12837    
12838     getButtonContainer : function() /// what children are added to.
12839     {
12840         return this.el.select(".roo-card-uploader-button-container").first();
12841     },
12842    
12843     initEvents : function()
12844     {
12845         
12846         Roo.bootstrap.Input.prototype.initEvents.call(this);
12847         
12848         var t = this;
12849         this.addxtype({
12850             xns: Roo.bootstrap,
12851
12852             xtype : 'Button',
12853             container_method : 'getButtonContainer' ,            
12854             html :  this.html, // fix changable?
12855             cls : 'w-100 ',
12856             listeners : {
12857                 'click' : function(btn, e) {
12858                     t.onClick(e);
12859                 }
12860             }
12861         });
12862         
12863         
12864         
12865         
12866         this.urlAPI = (window.createObjectURL && window) || 
12867                                 (window.URL && URL.revokeObjectURL && URL) || 
12868                                 (window.webkitURL && webkitURL);
12869                         
12870          
12871          
12872          
12873         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12874         
12875         this.selectorEl.on('change', this.onFileSelected, this);
12876         if (this.images) {
12877             var t = this;
12878             this.images.forEach(function(img) {
12879                 t.addCard(img)
12880             });
12881             this.images = false;
12882         }
12883         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12884          
12885        
12886     },
12887     
12888    
12889     onClick : function(e)
12890     {
12891         e.preventDefault();
12892          
12893         this.selectorEl.dom.click();
12894          
12895     },
12896     
12897     onFileSelected : function(e)
12898     {
12899         e.preventDefault();
12900         
12901         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12902             return;
12903         }
12904         
12905         Roo.each(this.selectorEl.dom.files, function(file){    
12906             this.addFile(file);
12907         }, this);
12908          
12909     },
12910     
12911       
12912     
12913       
12914     
12915     addFile : function(file)
12916     {
12917            
12918         if(typeof(file) === 'string'){
12919             throw "Add file by name?"; // should not happen
12920             return;
12921         }
12922         
12923         if(!file || !this.urlAPI){
12924             return;
12925         }
12926         
12927         // file;
12928         // file.type;
12929         
12930         var _this = this;
12931         
12932         
12933         var url = _this.urlAPI.createObjectURL( file);
12934            
12935         this.addCard({
12936             id : Roo.bootstrap.CardUploader.ID--,
12937             is_uploaded : false,
12938             src : url,
12939             srcfile : file,
12940             title : file.name,
12941             mimetype : file.type,
12942             preview : false,
12943             is_deleted : 0
12944         });
12945         
12946     },
12947     
12948     /**
12949      * addCard - add an Attachment to the uploader
12950      * @param data - the data about the image to upload
12951      *
12952      * {
12953           id : 123
12954           title : "Title of file",
12955           is_uploaded : false,
12956           src : "http://.....",
12957           srcfile : { the File upload object },
12958           mimetype : file.type,
12959           preview : false,
12960           is_deleted : 0
12961           .. any other data...
12962         }
12963      *
12964      * 
12965     */
12966     
12967     addCard : function (data)
12968     {
12969         // hidden input element?
12970         // if the file is not an image...
12971         //then we need to use something other that and header_image
12972         var t = this;
12973         //   remove.....
12974         var footer = [
12975             {
12976                 xns : Roo.bootstrap,
12977                 xtype : 'CardFooter',
12978                  items: [
12979                     {
12980                         xns : Roo.bootstrap,
12981                         xtype : 'Element',
12982                         cls : 'd-flex',
12983                         items : [
12984                             
12985                             {
12986                                 xns : Roo.bootstrap,
12987                                 xtype : 'Button',
12988                                 html : String.format("<small>{0}</small>", data.title),
12989                                 cls : 'col-10 text-left',
12990                                 size: 'sm',
12991                                 weight: 'link',
12992                                 fa : 'download',
12993                                 listeners : {
12994                                     click : function() {
12995                                      
12996                                         t.fireEvent( "download", t, data );
12997                                     }
12998                                 }
12999                             },
13000                           
13001                             {
13002                                 xns : Roo.bootstrap,
13003                                 xtype : 'Button',
13004                                 style: 'max-height: 28px; ',
13005                                 size : 'sm',
13006                                 weight: 'danger',
13007                                 cls : 'col-2',
13008                                 fa : 'times',
13009                                 listeners : {
13010                                     click : function() {
13011                                         t.removeCard(data.id)
13012                                     }
13013                                 }
13014                             }
13015                         ]
13016                     }
13017                     
13018                 ] 
13019             }
13020             
13021         ];
13022         
13023         var cn = this.addxtype(
13024             {
13025                  
13026                 xns : Roo.bootstrap,
13027                 xtype : 'Card',
13028                 closeable : true,
13029                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13030                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13031                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13032                 data : data,
13033                 html : false,
13034                  
13035                 items : footer,
13036                 initEvents : function() {
13037                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13038                     var card = this;
13039                     this.imgEl = this.el.select('.card-img-top').first();
13040                     if (this.imgEl) {
13041                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13042                         this.imgEl.set({ 'pointer' : 'cursor' });
13043                                   
13044                     }
13045                     this.getCardFooter().addClass('p-1');
13046                     
13047                   
13048                 }
13049                 
13050             }
13051         );
13052         // dont' really need ot update items.
13053         // this.items.push(cn);
13054         this.fileCollection.add(cn);
13055         
13056         if (!data.srcfile) {
13057             this.updateInput();
13058             return;
13059         }
13060             
13061         var _t = this;
13062         var reader = new FileReader();
13063         reader.addEventListener("load", function() {  
13064             data.srcdata =  reader.result;
13065             _t.updateInput();
13066         });
13067         reader.readAsDataURL(data.srcfile);
13068         
13069         
13070         
13071     },
13072     removeCard : function(id)
13073     {
13074         
13075         var card  = this.fileCollection.get(id);
13076         card.data.is_deleted = 1;
13077         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13078         //this.fileCollection.remove(card);
13079         //this.items = this.items.filter(function(e) { return e != card });
13080         // dont' really need ot update items.
13081         card.el.dom.parentNode.removeChild(card.el.dom);
13082         this.updateInput();
13083
13084         
13085     },
13086     reset: function()
13087     {
13088         this.fileCollection.each(function(card) {
13089             if (card.el.dom && card.el.dom.parentNode) {
13090                 card.el.dom.parentNode.removeChild(card.el.dom);
13091             }
13092         });
13093         this.fileCollection.clear();
13094         this.updateInput();
13095     },
13096     
13097     updateInput : function()
13098     {
13099          var data = [];
13100         this.fileCollection.each(function(e) {
13101             data.push(e.data);
13102             
13103         });
13104         this.inputEl().dom.value = JSON.stringify(data);
13105         
13106         
13107         
13108     }
13109     
13110     
13111 });
13112
13113
13114 Roo.bootstrap.CardUploader.ID = -1;/*
13115  * Based on:
13116  * Ext JS Library 1.1.1
13117  * Copyright(c) 2006-2007, Ext JS, LLC.
13118  *
13119  * Originally Released Under LGPL - original licence link has changed is not relivant.
13120  *
13121  * Fork - LGPL
13122  * <script type="text/javascript">
13123  */
13124
13125
13126 /**
13127  * @class Roo.data.SortTypes
13128  * @singleton
13129  * Defines the default sorting (casting?) comparison functions used when sorting data.
13130  */
13131 Roo.data.SortTypes = {
13132     /**
13133      * Default sort that does nothing
13134      * @param {Mixed} s The value being converted
13135      * @return {Mixed} The comparison value
13136      */
13137     none : function(s){
13138         return s;
13139     },
13140     
13141     /**
13142      * The regular expression used to strip tags
13143      * @type {RegExp}
13144      * @property
13145      */
13146     stripTagsRE : /<\/?[^>]+>/gi,
13147     
13148     /**
13149      * Strips all HTML tags to sort on text only
13150      * @param {Mixed} s The value being converted
13151      * @return {String} The comparison value
13152      */
13153     asText : function(s){
13154         return String(s).replace(this.stripTagsRE, "");
13155     },
13156     
13157     /**
13158      * Strips all HTML tags to sort on text only - Case insensitive
13159      * @param {Mixed} s The value being converted
13160      * @return {String} The comparison value
13161      */
13162     asUCText : function(s){
13163         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13164     },
13165     
13166     /**
13167      * Case insensitive string
13168      * @param {Mixed} s The value being converted
13169      * @return {String} The comparison value
13170      */
13171     asUCString : function(s) {
13172         return String(s).toUpperCase();
13173     },
13174     
13175     /**
13176      * Date sorting
13177      * @param {Mixed} s The value being converted
13178      * @return {Number} The comparison value
13179      */
13180     asDate : function(s) {
13181         if(!s){
13182             return 0;
13183         }
13184         if(s instanceof Date){
13185             return s.getTime();
13186         }
13187         return Date.parse(String(s));
13188     },
13189     
13190     /**
13191      * Float sorting
13192      * @param {Mixed} s The value being converted
13193      * @return {Float} The comparison value
13194      */
13195     asFloat : function(s) {
13196         var val = parseFloat(String(s).replace(/,/g, ""));
13197         if(isNaN(val)) {
13198             val = 0;
13199         }
13200         return val;
13201     },
13202     
13203     /**
13204      * Integer sorting
13205      * @param {Mixed} s The value being converted
13206      * @return {Number} The comparison value
13207      */
13208     asInt : function(s) {
13209         var val = parseInt(String(s).replace(/,/g, ""));
13210         if(isNaN(val)) {
13211             val = 0;
13212         }
13213         return val;
13214     }
13215 };/*
13216  * Based on:
13217  * Ext JS Library 1.1.1
13218  * Copyright(c) 2006-2007, Ext JS, LLC.
13219  *
13220  * Originally Released Under LGPL - original licence link has changed is not relivant.
13221  *
13222  * Fork - LGPL
13223  * <script type="text/javascript">
13224  */
13225
13226 /**
13227 * @class Roo.data.Record
13228  * Instances of this class encapsulate both record <em>definition</em> information, and record
13229  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13230  * to access Records cached in an {@link Roo.data.Store} object.<br>
13231  * <p>
13232  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13233  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13234  * objects.<br>
13235  * <p>
13236  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13237  * @constructor
13238  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13239  * {@link #create}. The parameters are the same.
13240  * @param {Array} data An associative Array of data values keyed by the field name.
13241  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13242  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13243  * not specified an integer id is generated.
13244  */
13245 Roo.data.Record = function(data, id){
13246     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13247     this.data = data;
13248 };
13249
13250 /**
13251  * Generate a constructor for a specific record layout.
13252  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13253  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13254  * Each field definition object may contain the following properties: <ul>
13255  * <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,
13256  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13257  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13258  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13259  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13260  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13261  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13262  * this may be omitted.</p></li>
13263  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13264  * <ul><li>auto (Default, implies no conversion)</li>
13265  * <li>string</li>
13266  * <li>int</li>
13267  * <li>float</li>
13268  * <li>boolean</li>
13269  * <li>date</li></ul></p></li>
13270  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13271  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13272  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13273  * by the Reader into an object that will be stored in the Record. It is passed the
13274  * following parameters:<ul>
13275  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13276  * </ul></p></li>
13277  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13278  * </ul>
13279  * <br>usage:<br><pre><code>
13280 var TopicRecord = Roo.data.Record.create(
13281     {name: 'title', mapping: 'topic_title'},
13282     {name: 'author', mapping: 'username'},
13283     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13284     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13285     {name: 'lastPoster', mapping: 'user2'},
13286     {name: 'excerpt', mapping: 'post_text'}
13287 );
13288
13289 var myNewRecord = new TopicRecord({
13290     title: 'Do my job please',
13291     author: 'noobie',
13292     totalPosts: 1,
13293     lastPost: new Date(),
13294     lastPoster: 'Animal',
13295     excerpt: 'No way dude!'
13296 });
13297 myStore.add(myNewRecord);
13298 </code></pre>
13299  * @method create
13300  * @static
13301  */
13302 Roo.data.Record.create = function(o){
13303     var f = function(){
13304         f.superclass.constructor.apply(this, arguments);
13305     };
13306     Roo.extend(f, Roo.data.Record);
13307     var p = f.prototype;
13308     p.fields = new Roo.util.MixedCollection(false, function(field){
13309         return field.name;
13310     });
13311     for(var i = 0, len = o.length; i < len; i++){
13312         p.fields.add(new Roo.data.Field(o[i]));
13313     }
13314     f.getField = function(name){
13315         return p.fields.get(name);  
13316     };
13317     return f;
13318 };
13319
13320 Roo.data.Record.AUTO_ID = 1000;
13321 Roo.data.Record.EDIT = 'edit';
13322 Roo.data.Record.REJECT = 'reject';
13323 Roo.data.Record.COMMIT = 'commit';
13324
13325 Roo.data.Record.prototype = {
13326     /**
13327      * Readonly flag - true if this record has been modified.
13328      * @type Boolean
13329      */
13330     dirty : false,
13331     editing : false,
13332     error: null,
13333     modified: null,
13334
13335     // private
13336     join : function(store){
13337         this.store = store;
13338     },
13339
13340     /**
13341      * Set the named field to the specified value.
13342      * @param {String} name The name of the field to set.
13343      * @param {Object} value The value to set the field to.
13344      */
13345     set : function(name, value){
13346         if(this.data[name] == value){
13347             return;
13348         }
13349         this.dirty = true;
13350         if(!this.modified){
13351             this.modified = {};
13352         }
13353         if(typeof this.modified[name] == 'undefined'){
13354             this.modified[name] = this.data[name];
13355         }
13356         this.data[name] = value;
13357         if(!this.editing && this.store){
13358             this.store.afterEdit(this);
13359         }       
13360     },
13361
13362     /**
13363      * Get the value of the named field.
13364      * @param {String} name The name of the field to get the value of.
13365      * @return {Object} The value of the field.
13366      */
13367     get : function(name){
13368         return this.data[name]; 
13369     },
13370
13371     // private
13372     beginEdit : function(){
13373         this.editing = true;
13374         this.modified = {}; 
13375     },
13376
13377     // private
13378     cancelEdit : function(){
13379         this.editing = false;
13380         delete this.modified;
13381     },
13382
13383     // private
13384     endEdit : function(){
13385         this.editing = false;
13386         if(this.dirty && this.store){
13387             this.store.afterEdit(this);
13388         }
13389     },
13390
13391     /**
13392      * Usually called by the {@link Roo.data.Store} which owns the Record.
13393      * Rejects all changes made to the Record since either creation, or the last commit operation.
13394      * Modified fields are reverted to their original values.
13395      * <p>
13396      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13397      * of reject operations.
13398      */
13399     reject : function(){
13400         var m = this.modified;
13401         for(var n in m){
13402             if(typeof m[n] != "function"){
13403                 this.data[n] = m[n];
13404             }
13405         }
13406         this.dirty = false;
13407         delete this.modified;
13408         this.editing = false;
13409         if(this.store){
13410             this.store.afterReject(this);
13411         }
13412     },
13413
13414     /**
13415      * Usually called by the {@link Roo.data.Store} which owns the Record.
13416      * Commits all changes made to the Record since either creation, or the last commit operation.
13417      * <p>
13418      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13419      * of commit operations.
13420      */
13421     commit : function(){
13422         this.dirty = false;
13423         delete this.modified;
13424         this.editing = false;
13425         if(this.store){
13426             this.store.afterCommit(this);
13427         }
13428     },
13429
13430     // private
13431     hasError : function(){
13432         return this.error != null;
13433     },
13434
13435     // private
13436     clearError : function(){
13437         this.error = null;
13438     },
13439
13440     /**
13441      * Creates a copy of this record.
13442      * @param {String} id (optional) A new record id if you don't want to use this record's id
13443      * @return {Record}
13444      */
13445     copy : function(newId) {
13446         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13447     }
13448 };/*
13449  * Based on:
13450  * Ext JS Library 1.1.1
13451  * Copyright(c) 2006-2007, Ext JS, LLC.
13452  *
13453  * Originally Released Under LGPL - original licence link has changed is not relivant.
13454  *
13455  * Fork - LGPL
13456  * <script type="text/javascript">
13457  */
13458
13459
13460
13461 /**
13462  * @class Roo.data.Store
13463  * @extends Roo.util.Observable
13464  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13465  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13466  * <p>
13467  * 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
13468  * has no knowledge of the format of the data returned by the Proxy.<br>
13469  * <p>
13470  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13471  * instances from the data object. These records are cached and made available through accessor functions.
13472  * @constructor
13473  * Creates a new Store.
13474  * @param {Object} config A config object containing the objects needed for the Store to access data,
13475  * and read the data into Records.
13476  */
13477 Roo.data.Store = function(config){
13478     this.data = new Roo.util.MixedCollection(false);
13479     this.data.getKey = function(o){
13480         return o.id;
13481     };
13482     this.baseParams = {};
13483     // private
13484     this.paramNames = {
13485         "start" : "start",
13486         "limit" : "limit",
13487         "sort" : "sort",
13488         "dir" : "dir",
13489         "multisort" : "_multisort"
13490     };
13491
13492     if(config && config.data){
13493         this.inlineData = config.data;
13494         delete config.data;
13495     }
13496
13497     Roo.apply(this, config);
13498     
13499     if(this.reader){ // reader passed
13500         this.reader = Roo.factory(this.reader, Roo.data);
13501         this.reader.xmodule = this.xmodule || false;
13502         if(!this.recordType){
13503             this.recordType = this.reader.recordType;
13504         }
13505         if(this.reader.onMetaChange){
13506             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13507         }
13508     }
13509
13510     if(this.recordType){
13511         this.fields = this.recordType.prototype.fields;
13512     }
13513     this.modified = [];
13514
13515     this.addEvents({
13516         /**
13517          * @event datachanged
13518          * Fires when the data cache has changed, and a widget which is using this Store
13519          * as a Record cache should refresh its view.
13520          * @param {Store} this
13521          */
13522         datachanged : true,
13523         /**
13524          * @event metachange
13525          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13526          * @param {Store} this
13527          * @param {Object} meta The JSON metadata
13528          */
13529         metachange : true,
13530         /**
13531          * @event add
13532          * Fires when Records have been added to the Store
13533          * @param {Store} this
13534          * @param {Roo.data.Record[]} records The array of Records added
13535          * @param {Number} index The index at which the record(s) were added
13536          */
13537         add : true,
13538         /**
13539          * @event remove
13540          * Fires when a Record has been removed from the Store
13541          * @param {Store} this
13542          * @param {Roo.data.Record} record The Record that was removed
13543          * @param {Number} index The index at which the record was removed
13544          */
13545         remove : true,
13546         /**
13547          * @event update
13548          * Fires when a Record has been updated
13549          * @param {Store} this
13550          * @param {Roo.data.Record} record The Record that was updated
13551          * @param {String} operation The update operation being performed.  Value may be one of:
13552          * <pre><code>
13553  Roo.data.Record.EDIT
13554  Roo.data.Record.REJECT
13555  Roo.data.Record.COMMIT
13556          * </code></pre>
13557          */
13558         update : true,
13559         /**
13560          * @event clear
13561          * Fires when the data cache has been cleared.
13562          * @param {Store} this
13563          */
13564         clear : true,
13565         /**
13566          * @event beforeload
13567          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13568          * the load action will be canceled.
13569          * @param {Store} this
13570          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13571          */
13572         beforeload : true,
13573         /**
13574          * @event beforeloadadd
13575          * Fires after a new set of Records has been loaded.
13576          * @param {Store} this
13577          * @param {Roo.data.Record[]} records The Records that were loaded
13578          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13579          */
13580         beforeloadadd : true,
13581         /**
13582          * @event load
13583          * Fires after a new set of Records has been loaded, before they are added to the store.
13584          * @param {Store} this
13585          * @param {Roo.data.Record[]} records The Records that were loaded
13586          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13587          * @params {Object} return from reader
13588          */
13589         load : true,
13590         /**
13591          * @event loadexception
13592          * Fires if an exception occurs in the Proxy during loading.
13593          * Called with the signature of the Proxy's "loadexception" event.
13594          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13595          * 
13596          * @param {Proxy} 
13597          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13598          * @param {Object} load options 
13599          * @param {Object} jsonData from your request (normally this contains the Exception)
13600          */
13601         loadexception : true
13602     });
13603     
13604     if(this.proxy){
13605         this.proxy = Roo.factory(this.proxy, Roo.data);
13606         this.proxy.xmodule = this.xmodule || false;
13607         this.relayEvents(this.proxy,  ["loadexception"]);
13608     }
13609     this.sortToggle = {};
13610     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13611
13612     Roo.data.Store.superclass.constructor.call(this);
13613
13614     if(this.inlineData){
13615         this.loadData(this.inlineData);
13616         delete this.inlineData;
13617     }
13618 };
13619
13620 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13621      /**
13622     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13623     * without a remote query - used by combo/forms at present.
13624     */
13625     
13626     /**
13627     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13628     */
13629     /**
13630     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13631     */
13632     /**
13633     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13634     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13635     */
13636     /**
13637     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13638     * on any HTTP request
13639     */
13640     /**
13641     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13642     */
13643     /**
13644     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13645     */
13646     multiSort: false,
13647     /**
13648     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13649     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13650     */
13651     remoteSort : false,
13652
13653     /**
13654     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13655      * loaded or when a record is removed. (defaults to false).
13656     */
13657     pruneModifiedRecords : false,
13658
13659     // private
13660     lastOptions : null,
13661
13662     /**
13663      * Add Records to the Store and fires the add event.
13664      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13665      */
13666     add : function(records){
13667         records = [].concat(records);
13668         for(var i = 0, len = records.length; i < len; i++){
13669             records[i].join(this);
13670         }
13671         var index = this.data.length;
13672         this.data.addAll(records);
13673         this.fireEvent("add", this, records, index);
13674     },
13675
13676     /**
13677      * Remove a Record from the Store and fires the remove event.
13678      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13679      */
13680     remove : function(record){
13681         var index = this.data.indexOf(record);
13682         this.data.removeAt(index);
13683  
13684         if(this.pruneModifiedRecords){
13685             this.modified.remove(record);
13686         }
13687         this.fireEvent("remove", this, record, index);
13688     },
13689
13690     /**
13691      * Remove all Records from the Store and fires the clear event.
13692      */
13693     removeAll : function(){
13694         this.data.clear();
13695         if(this.pruneModifiedRecords){
13696             this.modified = [];
13697         }
13698         this.fireEvent("clear", this);
13699     },
13700
13701     /**
13702      * Inserts Records to the Store at the given index and fires the add event.
13703      * @param {Number} index The start index at which to insert the passed Records.
13704      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13705      */
13706     insert : function(index, records){
13707         records = [].concat(records);
13708         for(var i = 0, len = records.length; i < len; i++){
13709             this.data.insert(index, records[i]);
13710             records[i].join(this);
13711         }
13712         this.fireEvent("add", this, records, index);
13713     },
13714
13715     /**
13716      * Get the index within the cache of the passed Record.
13717      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13718      * @return {Number} The index of the passed Record. Returns -1 if not found.
13719      */
13720     indexOf : function(record){
13721         return this.data.indexOf(record);
13722     },
13723
13724     /**
13725      * Get the index within the cache of the Record with the passed id.
13726      * @param {String} id The id of the Record to find.
13727      * @return {Number} The index of the Record. Returns -1 if not found.
13728      */
13729     indexOfId : function(id){
13730         return this.data.indexOfKey(id);
13731     },
13732
13733     /**
13734      * Get the Record with the specified id.
13735      * @param {String} id The id of the Record to find.
13736      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13737      */
13738     getById : function(id){
13739         return this.data.key(id);
13740     },
13741
13742     /**
13743      * Get the Record at the specified index.
13744      * @param {Number} index The index of the Record to find.
13745      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13746      */
13747     getAt : function(index){
13748         return this.data.itemAt(index);
13749     },
13750
13751     /**
13752      * Returns a range of Records between specified indices.
13753      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13754      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13755      * @return {Roo.data.Record[]} An array of Records
13756      */
13757     getRange : function(start, end){
13758         return this.data.getRange(start, end);
13759     },
13760
13761     // private
13762     storeOptions : function(o){
13763         o = Roo.apply({}, o);
13764         delete o.callback;
13765         delete o.scope;
13766         this.lastOptions = o;
13767     },
13768
13769     /**
13770      * Loads the Record cache from the configured Proxy using the configured Reader.
13771      * <p>
13772      * If using remote paging, then the first load call must specify the <em>start</em>
13773      * and <em>limit</em> properties in the options.params property to establish the initial
13774      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13775      * <p>
13776      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13777      * and this call will return before the new data has been loaded. Perform any post-processing
13778      * in a callback function, or in a "load" event handler.</strong>
13779      * <p>
13780      * @param {Object} options An object containing properties which control loading options:<ul>
13781      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13782      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13783      * passed the following arguments:<ul>
13784      * <li>r : Roo.data.Record[]</li>
13785      * <li>options: Options object from the load call</li>
13786      * <li>success: Boolean success indicator</li></ul></li>
13787      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13788      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13789      * </ul>
13790      */
13791     load : function(options){
13792         options = options || {};
13793         if(this.fireEvent("beforeload", this, options) !== false){
13794             this.storeOptions(options);
13795             var p = Roo.apply(options.params || {}, this.baseParams);
13796             // if meta was not loaded from remote source.. try requesting it.
13797             if (!this.reader.metaFromRemote) {
13798                 p._requestMeta = 1;
13799             }
13800             if(this.sortInfo && this.remoteSort){
13801                 var pn = this.paramNames;
13802                 p[pn["sort"]] = this.sortInfo.field;
13803                 p[pn["dir"]] = this.sortInfo.direction;
13804             }
13805             if (this.multiSort) {
13806                 var pn = this.paramNames;
13807                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13808             }
13809             
13810             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13811         }
13812     },
13813
13814     /**
13815      * Reloads the Record cache from the configured Proxy using the configured Reader and
13816      * the options from the last load operation performed.
13817      * @param {Object} options (optional) An object containing properties which may override the options
13818      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13819      * the most recently used options are reused).
13820      */
13821     reload : function(options){
13822         this.load(Roo.applyIf(options||{}, this.lastOptions));
13823     },
13824
13825     // private
13826     // Called as a callback by the Reader during a load operation.
13827     loadRecords : function(o, options, success){
13828         if(!o || success === false){
13829             if(success !== false){
13830                 this.fireEvent("load", this, [], options, o);
13831             }
13832             if(options.callback){
13833                 options.callback.call(options.scope || this, [], options, false);
13834             }
13835             return;
13836         }
13837         // if data returned failure - throw an exception.
13838         if (o.success === false) {
13839             // show a message if no listener is registered.
13840             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13841                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13842             }
13843             // loadmask wil be hooked into this..
13844             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13845             return;
13846         }
13847         var r = o.records, t = o.totalRecords || r.length;
13848         
13849         this.fireEvent("beforeloadadd", this, r, options, o);
13850         
13851         if(!options || options.add !== true){
13852             if(this.pruneModifiedRecords){
13853                 this.modified = [];
13854             }
13855             for(var i = 0, len = r.length; i < len; i++){
13856                 r[i].join(this);
13857             }
13858             if(this.snapshot){
13859                 this.data = this.snapshot;
13860                 delete this.snapshot;
13861             }
13862             this.data.clear();
13863             this.data.addAll(r);
13864             this.totalLength = t;
13865             this.applySort();
13866             this.fireEvent("datachanged", this);
13867         }else{
13868             this.totalLength = Math.max(t, this.data.length+r.length);
13869             this.add(r);
13870         }
13871         
13872         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13873                 
13874             var e = new Roo.data.Record({});
13875
13876             e.set(this.parent.displayField, this.parent.emptyTitle);
13877             e.set(this.parent.valueField, '');
13878
13879             this.insert(0, e);
13880         }
13881             
13882         this.fireEvent("load", this, r, options, o);
13883         if(options.callback){
13884             options.callback.call(options.scope || this, r, options, true);
13885         }
13886     },
13887
13888
13889     /**
13890      * Loads data from a passed data block. A Reader which understands the format of the data
13891      * must have been configured in the constructor.
13892      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13893      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13894      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13895      */
13896     loadData : function(o, append){
13897         var r = this.reader.readRecords(o);
13898         this.loadRecords(r, {add: append}, true);
13899     },
13900     
13901      /**
13902      * using 'cn' the nested child reader read the child array into it's child stores.
13903      * @param {Object} rec The record with a 'children array
13904      */
13905     loadDataFromChildren : function(rec)
13906     {
13907         this.loadData(this.reader.toLoadData(rec));
13908     },
13909     
13910
13911     /**
13912      * Gets the number of cached records.
13913      * <p>
13914      * <em>If using paging, this may not be the total size of the dataset. If the data object
13915      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13916      * the data set size</em>
13917      */
13918     getCount : function(){
13919         return this.data.length || 0;
13920     },
13921
13922     /**
13923      * Gets the total number of records in the dataset as returned by the server.
13924      * <p>
13925      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13926      * the dataset size</em>
13927      */
13928     getTotalCount : function(){
13929         return this.totalLength || 0;
13930     },
13931
13932     /**
13933      * Returns the sort state of the Store as an object with two properties:
13934      * <pre><code>
13935  field {String} The name of the field by which the Records are sorted
13936  direction {String} The sort order, "ASC" or "DESC"
13937      * </code></pre>
13938      */
13939     getSortState : function(){
13940         return this.sortInfo;
13941     },
13942
13943     // private
13944     applySort : function(){
13945         if(this.sortInfo && !this.remoteSort){
13946             var s = this.sortInfo, f = s.field;
13947             var st = this.fields.get(f).sortType;
13948             var fn = function(r1, r2){
13949                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13950                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13951             };
13952             this.data.sort(s.direction, fn);
13953             if(this.snapshot && this.snapshot != this.data){
13954                 this.snapshot.sort(s.direction, fn);
13955             }
13956         }
13957     },
13958
13959     /**
13960      * Sets the default sort column and order to be used by the next load operation.
13961      * @param {String} fieldName The name of the field to sort by.
13962      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13963      */
13964     setDefaultSort : function(field, dir){
13965         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13966     },
13967
13968     /**
13969      * Sort the Records.
13970      * If remote sorting is used, the sort is performed on the server, and the cache is
13971      * reloaded. If local sorting is used, the cache is sorted internally.
13972      * @param {String} fieldName The name of the field to sort by.
13973      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13974      */
13975     sort : function(fieldName, dir){
13976         var f = this.fields.get(fieldName);
13977         if(!dir){
13978             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13979             
13980             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13981                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13982             }else{
13983                 dir = f.sortDir;
13984             }
13985         }
13986         this.sortToggle[f.name] = dir;
13987         this.sortInfo = {field: f.name, direction: dir};
13988         if(!this.remoteSort){
13989             this.applySort();
13990             this.fireEvent("datachanged", this);
13991         }else{
13992             this.load(this.lastOptions);
13993         }
13994     },
13995
13996     /**
13997      * Calls the specified function for each of the Records in the cache.
13998      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13999      * Returning <em>false</em> aborts and exits the iteration.
14000      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14001      */
14002     each : function(fn, scope){
14003         this.data.each(fn, scope);
14004     },
14005
14006     /**
14007      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14008      * (e.g., during paging).
14009      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14010      */
14011     getModifiedRecords : function(){
14012         return this.modified;
14013     },
14014
14015     // private
14016     createFilterFn : function(property, value, anyMatch){
14017         if(!value.exec){ // not a regex
14018             value = String(value);
14019             if(value.length == 0){
14020                 return false;
14021             }
14022             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14023         }
14024         return function(r){
14025             return value.test(r.data[property]);
14026         };
14027     },
14028
14029     /**
14030      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14031      * @param {String} property A field on your records
14032      * @param {Number} start The record index to start at (defaults to 0)
14033      * @param {Number} end The last record index to include (defaults to length - 1)
14034      * @return {Number} The sum
14035      */
14036     sum : function(property, start, end){
14037         var rs = this.data.items, v = 0;
14038         start = start || 0;
14039         end = (end || end === 0) ? end : rs.length-1;
14040
14041         for(var i = start; i <= end; i++){
14042             v += (rs[i].data[property] || 0);
14043         }
14044         return v;
14045     },
14046
14047     /**
14048      * Filter the records by a specified property.
14049      * @param {String} field A field on your records
14050      * @param {String/RegExp} value Either a string that the field
14051      * should start with or a RegExp to test against the field
14052      * @param {Boolean} anyMatch True to match any part not just the beginning
14053      */
14054     filter : function(property, value, anyMatch){
14055         var fn = this.createFilterFn(property, value, anyMatch);
14056         return fn ? this.filterBy(fn) : this.clearFilter();
14057     },
14058
14059     /**
14060      * Filter by a function. The specified function will be called with each
14061      * record in this data source. If the function returns true the record is included,
14062      * otherwise it is filtered.
14063      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14064      * @param {Object} scope (optional) The scope of the function (defaults to this)
14065      */
14066     filterBy : function(fn, scope){
14067         this.snapshot = this.snapshot || this.data;
14068         this.data = this.queryBy(fn, scope||this);
14069         this.fireEvent("datachanged", this);
14070     },
14071
14072     /**
14073      * Query the records by a specified property.
14074      * @param {String} field A field on your records
14075      * @param {String/RegExp} value Either a string that the field
14076      * should start with or a RegExp to test against the field
14077      * @param {Boolean} anyMatch True to match any part not just the beginning
14078      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14079      */
14080     query : function(property, value, anyMatch){
14081         var fn = this.createFilterFn(property, value, anyMatch);
14082         return fn ? this.queryBy(fn) : this.data.clone();
14083     },
14084
14085     /**
14086      * Query by a function. The specified function will be called with each
14087      * record in this data source. If the function returns true the record is included
14088      * in the results.
14089      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14090      * @param {Object} scope (optional) The scope of the function (defaults to this)
14091       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14092      **/
14093     queryBy : function(fn, scope){
14094         var data = this.snapshot || this.data;
14095         return data.filterBy(fn, scope||this);
14096     },
14097
14098     /**
14099      * Collects unique values for a particular dataIndex from this store.
14100      * @param {String} dataIndex The property to collect
14101      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14102      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14103      * @return {Array} An array of the unique values
14104      **/
14105     collect : function(dataIndex, allowNull, bypassFilter){
14106         var d = (bypassFilter === true && this.snapshot) ?
14107                 this.snapshot.items : this.data.items;
14108         var v, sv, r = [], l = {};
14109         for(var i = 0, len = d.length; i < len; i++){
14110             v = d[i].data[dataIndex];
14111             sv = String(v);
14112             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14113                 l[sv] = true;
14114                 r[r.length] = v;
14115             }
14116         }
14117         return r;
14118     },
14119
14120     /**
14121      * Revert to a view of the Record cache with no filtering applied.
14122      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14123      */
14124     clearFilter : function(suppressEvent){
14125         if(this.snapshot && this.snapshot != this.data){
14126             this.data = this.snapshot;
14127             delete this.snapshot;
14128             if(suppressEvent !== true){
14129                 this.fireEvent("datachanged", this);
14130             }
14131         }
14132     },
14133
14134     // private
14135     afterEdit : function(record){
14136         if(this.modified.indexOf(record) == -1){
14137             this.modified.push(record);
14138         }
14139         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14140     },
14141     
14142     // private
14143     afterReject : function(record){
14144         this.modified.remove(record);
14145         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14146     },
14147
14148     // private
14149     afterCommit : function(record){
14150         this.modified.remove(record);
14151         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14152     },
14153
14154     /**
14155      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14156      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14157      */
14158     commitChanges : function(){
14159         var m = this.modified.slice(0);
14160         this.modified = [];
14161         for(var i = 0, len = m.length; i < len; i++){
14162             m[i].commit();
14163         }
14164     },
14165
14166     /**
14167      * Cancel outstanding changes on all changed records.
14168      */
14169     rejectChanges : function(){
14170         var m = this.modified.slice(0);
14171         this.modified = [];
14172         for(var i = 0, len = m.length; i < len; i++){
14173             m[i].reject();
14174         }
14175     },
14176
14177     onMetaChange : function(meta, rtype, o){
14178         this.recordType = rtype;
14179         this.fields = rtype.prototype.fields;
14180         delete this.snapshot;
14181         this.sortInfo = meta.sortInfo || this.sortInfo;
14182         this.modified = [];
14183         this.fireEvent('metachange', this, this.reader.meta);
14184     },
14185     
14186     moveIndex : function(data, type)
14187     {
14188         var index = this.indexOf(data);
14189         
14190         var newIndex = index + type;
14191         
14192         this.remove(data);
14193         
14194         this.insert(newIndex, data);
14195         
14196     }
14197 });/*
14198  * Based on:
14199  * Ext JS Library 1.1.1
14200  * Copyright(c) 2006-2007, Ext JS, LLC.
14201  *
14202  * Originally Released Under LGPL - original licence link has changed is not relivant.
14203  *
14204  * Fork - LGPL
14205  * <script type="text/javascript">
14206  */
14207
14208 /**
14209  * @class Roo.data.SimpleStore
14210  * @extends Roo.data.Store
14211  * Small helper class to make creating Stores from Array data easier.
14212  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14213  * @cfg {Array} fields An array of field definition objects, or field name strings.
14214  * @cfg {Object} an existing reader (eg. copied from another store)
14215  * @cfg {Array} data The multi-dimensional array of data
14216  * @constructor
14217  * @param {Object} config
14218  */
14219 Roo.data.SimpleStore = function(config)
14220 {
14221     Roo.data.SimpleStore.superclass.constructor.call(this, {
14222         isLocal : true,
14223         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14224                 id: config.id
14225             },
14226             Roo.data.Record.create(config.fields)
14227         ),
14228         proxy : new Roo.data.MemoryProxy(config.data)
14229     });
14230     this.load();
14231 };
14232 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14233  * Based on:
14234  * Ext JS Library 1.1.1
14235  * Copyright(c) 2006-2007, Ext JS, LLC.
14236  *
14237  * Originally Released Under LGPL - original licence link has changed is not relivant.
14238  *
14239  * Fork - LGPL
14240  * <script type="text/javascript">
14241  */
14242
14243 /**
14244 /**
14245  * @extends Roo.data.Store
14246  * @class Roo.data.JsonStore
14247  * Small helper class to make creating Stores for JSON data easier. <br/>
14248 <pre><code>
14249 var store = new Roo.data.JsonStore({
14250     url: 'get-images.php',
14251     root: 'images',
14252     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14253 });
14254 </code></pre>
14255  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14256  * JsonReader and HttpProxy (unless inline data is provided).</b>
14257  * @cfg {Array} fields An array of field definition objects, or field name strings.
14258  * @constructor
14259  * @param {Object} config
14260  */
14261 Roo.data.JsonStore = function(c){
14262     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14263         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14264         reader: new Roo.data.JsonReader(c, c.fields)
14265     }));
14266 };
14267 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14268  * Based on:
14269  * Ext JS Library 1.1.1
14270  * Copyright(c) 2006-2007, Ext JS, LLC.
14271  *
14272  * Originally Released Under LGPL - original licence link has changed is not relivant.
14273  *
14274  * Fork - LGPL
14275  * <script type="text/javascript">
14276  */
14277
14278  
14279 Roo.data.Field = function(config){
14280     if(typeof config == "string"){
14281         config = {name: config};
14282     }
14283     Roo.apply(this, config);
14284     
14285     if(!this.type){
14286         this.type = "auto";
14287     }
14288     
14289     var st = Roo.data.SortTypes;
14290     // named sortTypes are supported, here we look them up
14291     if(typeof this.sortType == "string"){
14292         this.sortType = st[this.sortType];
14293     }
14294     
14295     // set default sortType for strings and dates
14296     if(!this.sortType){
14297         switch(this.type){
14298             case "string":
14299                 this.sortType = st.asUCString;
14300                 break;
14301             case "date":
14302                 this.sortType = st.asDate;
14303                 break;
14304             default:
14305                 this.sortType = st.none;
14306         }
14307     }
14308
14309     // define once
14310     var stripRe = /[\$,%]/g;
14311
14312     // prebuilt conversion function for this field, instead of
14313     // switching every time we're reading a value
14314     if(!this.convert){
14315         var cv, dateFormat = this.dateFormat;
14316         switch(this.type){
14317             case "":
14318             case "auto":
14319             case undefined:
14320                 cv = function(v){ return v; };
14321                 break;
14322             case "string":
14323                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14324                 break;
14325             case "int":
14326                 cv = function(v){
14327                     return v !== undefined && v !== null && v !== '' ?
14328                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14329                     };
14330                 break;
14331             case "float":
14332                 cv = function(v){
14333                     return v !== undefined && v !== null && v !== '' ?
14334                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14335                     };
14336                 break;
14337             case "bool":
14338             case "boolean":
14339                 cv = function(v){ return v === true || v === "true" || v == 1; };
14340                 break;
14341             case "date":
14342                 cv = function(v){
14343                     if(!v){
14344                         return '';
14345                     }
14346                     if(v instanceof Date){
14347                         return v;
14348                     }
14349                     if(dateFormat){
14350                         if(dateFormat == "timestamp"){
14351                             return new Date(v*1000);
14352                         }
14353                         return Date.parseDate(v, dateFormat);
14354                     }
14355                     var parsed = Date.parse(v);
14356                     return parsed ? new Date(parsed) : null;
14357                 };
14358              break;
14359             
14360         }
14361         this.convert = cv;
14362     }
14363 };
14364
14365 Roo.data.Field.prototype = {
14366     dateFormat: null,
14367     defaultValue: "",
14368     mapping: null,
14369     sortType : null,
14370     sortDir : "ASC"
14371 };/*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381  
14382 // Base class for reading structured data from a data source.  This class is intended to be
14383 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14384
14385 /**
14386  * @class Roo.data.DataReader
14387  * Base class for reading structured data from a data source.  This class is intended to be
14388  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14389  */
14390
14391 Roo.data.DataReader = function(meta, recordType){
14392     
14393     this.meta = meta;
14394     
14395     this.recordType = recordType instanceof Array ? 
14396         Roo.data.Record.create(recordType) : recordType;
14397 };
14398
14399 Roo.data.DataReader.prototype = {
14400     
14401     
14402     readerType : 'Data',
14403      /**
14404      * Create an empty record
14405      * @param {Object} data (optional) - overlay some values
14406      * @return {Roo.data.Record} record created.
14407      */
14408     newRow :  function(d) {
14409         var da =  {};
14410         this.recordType.prototype.fields.each(function(c) {
14411             switch( c.type) {
14412                 case 'int' : da[c.name] = 0; break;
14413                 case 'date' : da[c.name] = new Date(); break;
14414                 case 'float' : da[c.name] = 0.0; break;
14415                 case 'boolean' : da[c.name] = false; break;
14416                 default : da[c.name] = ""; break;
14417             }
14418             
14419         });
14420         return new this.recordType(Roo.apply(da, d));
14421     }
14422     
14423     
14424 };/*
14425  * Based on:
14426  * Ext JS Library 1.1.1
14427  * Copyright(c) 2006-2007, Ext JS, LLC.
14428  *
14429  * Originally Released Under LGPL - original licence link has changed is not relivant.
14430  *
14431  * Fork - LGPL
14432  * <script type="text/javascript">
14433  */
14434
14435 /**
14436  * @class Roo.data.DataProxy
14437  * @extends Roo.data.Observable
14438  * This class is an abstract base class for implementations which provide retrieval of
14439  * unformatted data objects.<br>
14440  * <p>
14441  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14442  * (of the appropriate type which knows how to parse the data object) to provide a block of
14443  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14444  * <p>
14445  * Custom implementations must implement the load method as described in
14446  * {@link Roo.data.HttpProxy#load}.
14447  */
14448 Roo.data.DataProxy = function(){
14449     this.addEvents({
14450         /**
14451          * @event beforeload
14452          * Fires before a network request is made to retrieve a data object.
14453          * @param {Object} This DataProxy object.
14454          * @param {Object} params The params parameter to the load function.
14455          */
14456         beforeload : true,
14457         /**
14458          * @event load
14459          * Fires before the load method's callback is called.
14460          * @param {Object} This DataProxy object.
14461          * @param {Object} o The data object.
14462          * @param {Object} arg The callback argument object passed to the load function.
14463          */
14464         load : true,
14465         /**
14466          * @event loadexception
14467          * Fires if an Exception occurs during data retrieval.
14468          * @param {Object} This DataProxy object.
14469          * @param {Object} o The data object.
14470          * @param {Object} arg The callback argument object passed to the load function.
14471          * @param {Object} e The Exception.
14472          */
14473         loadexception : true
14474     });
14475     Roo.data.DataProxy.superclass.constructor.call(this);
14476 };
14477
14478 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14479
14480     /**
14481      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14482      */
14483 /*
14484  * Based on:
14485  * Ext JS Library 1.1.1
14486  * Copyright(c) 2006-2007, Ext JS, LLC.
14487  *
14488  * Originally Released Under LGPL - original licence link has changed is not relivant.
14489  *
14490  * Fork - LGPL
14491  * <script type="text/javascript">
14492  */
14493 /**
14494  * @class Roo.data.MemoryProxy
14495  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14496  * to the Reader when its load method is called.
14497  * @constructor
14498  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14499  */
14500 Roo.data.MemoryProxy = function(data){
14501     if (data.data) {
14502         data = data.data;
14503     }
14504     Roo.data.MemoryProxy.superclass.constructor.call(this);
14505     this.data = data;
14506 };
14507
14508 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14509     
14510     /**
14511      * Load data from the requested source (in this case an in-memory
14512      * data object passed to the constructor), read the data object into
14513      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14514      * process that block using the passed callback.
14515      * @param {Object} params This parameter is not used by the MemoryProxy class.
14516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14517      * object into a block of Roo.data.Records.
14518      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14519      * The function must be passed <ul>
14520      * <li>The Record block object</li>
14521      * <li>The "arg" argument from the load function</li>
14522      * <li>A boolean success indicator</li>
14523      * </ul>
14524      * @param {Object} scope The scope in which to call the callback
14525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14526      */
14527     load : function(params, reader, callback, scope, arg){
14528         params = params || {};
14529         var result;
14530         try {
14531             result = reader.readRecords(params.data ? params.data :this.data);
14532         }catch(e){
14533             this.fireEvent("loadexception", this, arg, null, e);
14534             callback.call(scope, null, arg, false);
14535             return;
14536         }
14537         callback.call(scope, result, arg, true);
14538     },
14539     
14540     // private
14541     update : function(params, records){
14542         
14543     }
14544 });/*
14545  * Based on:
14546  * Ext JS Library 1.1.1
14547  * Copyright(c) 2006-2007, Ext JS, LLC.
14548  *
14549  * Originally Released Under LGPL - original licence link has changed is not relivant.
14550  *
14551  * Fork - LGPL
14552  * <script type="text/javascript">
14553  */
14554 /**
14555  * @class Roo.data.HttpProxy
14556  * @extends Roo.data.DataProxy
14557  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14558  * configured to reference a certain URL.<br><br>
14559  * <p>
14560  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14561  * from which the running page was served.<br><br>
14562  * <p>
14563  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14564  * <p>
14565  * Be aware that to enable the browser to parse an XML document, the server must set
14566  * the Content-Type header in the HTTP response to "text/xml".
14567  * @constructor
14568  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14569  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14570  * will be used to make the request.
14571  */
14572 Roo.data.HttpProxy = function(conn){
14573     Roo.data.HttpProxy.superclass.constructor.call(this);
14574     // is conn a conn config or a real conn?
14575     this.conn = conn;
14576     this.useAjax = !conn || !conn.events;
14577   
14578 };
14579
14580 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14581     // thse are take from connection...
14582     
14583     /**
14584      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14585      */
14586     /**
14587      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14588      * extra parameters to each request made by this object. (defaults to undefined)
14589      */
14590     /**
14591      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14592      *  to each request made by this object. (defaults to undefined)
14593      */
14594     /**
14595      * @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)
14596      */
14597     /**
14598      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14599      */
14600      /**
14601      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14602      * @type Boolean
14603      */
14604   
14605
14606     /**
14607      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14608      * @type Boolean
14609      */
14610     /**
14611      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14612      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14613      * a finer-grained basis than the DataProxy events.
14614      */
14615     getConnection : function(){
14616         return this.useAjax ? Roo.Ajax : this.conn;
14617     },
14618
14619     /**
14620      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14621      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14622      * process that block using the passed callback.
14623      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14624      * for the request to the remote server.
14625      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14626      * object into a block of Roo.data.Records.
14627      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14628      * The function must be passed <ul>
14629      * <li>The Record block object</li>
14630      * <li>The "arg" argument from the load function</li>
14631      * <li>A boolean success indicator</li>
14632      * </ul>
14633      * @param {Object} scope The scope in which to call the callback
14634      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14635      */
14636     load : function(params, reader, callback, scope, arg){
14637         if(this.fireEvent("beforeload", this, params) !== false){
14638             var  o = {
14639                 params : params || {},
14640                 request: {
14641                     callback : callback,
14642                     scope : scope,
14643                     arg : arg
14644                 },
14645                 reader: reader,
14646                 callback : this.loadResponse,
14647                 scope: this
14648             };
14649             if(this.useAjax){
14650                 Roo.applyIf(o, this.conn);
14651                 if(this.activeRequest){
14652                     Roo.Ajax.abort(this.activeRequest);
14653                 }
14654                 this.activeRequest = Roo.Ajax.request(o);
14655             }else{
14656                 this.conn.request(o);
14657             }
14658         }else{
14659             callback.call(scope||this, null, arg, false);
14660         }
14661     },
14662
14663     // private
14664     loadResponse : function(o, success, response){
14665         delete this.activeRequest;
14666         if(!success){
14667             this.fireEvent("loadexception", this, o, response);
14668             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14669             return;
14670         }
14671         var result;
14672         try {
14673             result = o.reader.read(response);
14674         }catch(e){
14675             this.fireEvent("loadexception", this, o, response, e);
14676             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14677             return;
14678         }
14679         
14680         this.fireEvent("load", this, o, o.request.arg);
14681         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14682     },
14683
14684     // private
14685     update : function(dataSet){
14686
14687     },
14688
14689     // private
14690     updateResponse : function(dataSet){
14691
14692     }
14693 });/*
14694  * Based on:
14695  * Ext JS Library 1.1.1
14696  * Copyright(c) 2006-2007, Ext JS, LLC.
14697  *
14698  * Originally Released Under LGPL - original licence link has changed is not relivant.
14699  *
14700  * Fork - LGPL
14701  * <script type="text/javascript">
14702  */
14703
14704 /**
14705  * @class Roo.data.ScriptTagProxy
14706  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14707  * other than the originating domain of the running page.<br><br>
14708  * <p>
14709  * <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
14710  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14711  * <p>
14712  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14713  * source code that is used as the source inside a &lt;script> tag.<br><br>
14714  * <p>
14715  * In order for the browser to process the returned data, the server must wrap the data object
14716  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14717  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14718  * depending on whether the callback name was passed:
14719  * <p>
14720  * <pre><code>
14721 boolean scriptTag = false;
14722 String cb = request.getParameter("callback");
14723 if (cb != null) {
14724     scriptTag = true;
14725     response.setContentType("text/javascript");
14726 } else {
14727     response.setContentType("application/x-json");
14728 }
14729 Writer out = response.getWriter();
14730 if (scriptTag) {
14731     out.write(cb + "(");
14732 }
14733 out.print(dataBlock.toJsonString());
14734 if (scriptTag) {
14735     out.write(");");
14736 }
14737 </pre></code>
14738  *
14739  * @constructor
14740  * @param {Object} config A configuration object.
14741  */
14742 Roo.data.ScriptTagProxy = function(config){
14743     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14744     Roo.apply(this, config);
14745     this.head = document.getElementsByTagName("head")[0];
14746 };
14747
14748 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14749
14750 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14751     /**
14752      * @cfg {String} url The URL from which to request the data object.
14753      */
14754     /**
14755      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14756      */
14757     timeout : 30000,
14758     /**
14759      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14760      * the server the name of the callback function set up by the load call to process the returned data object.
14761      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14762      * javascript output which calls this named function passing the data object as its only parameter.
14763      */
14764     callbackParam : "callback",
14765     /**
14766      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14767      * name to the request.
14768      */
14769     nocache : true,
14770
14771     /**
14772      * Load data from the configured URL, read the data object into
14773      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14774      * process that block using the passed callback.
14775      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14776      * for the request to the remote server.
14777      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14778      * object into a block of Roo.data.Records.
14779      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14780      * The function must be passed <ul>
14781      * <li>The Record block object</li>
14782      * <li>The "arg" argument from the load function</li>
14783      * <li>A boolean success indicator</li>
14784      * </ul>
14785      * @param {Object} scope The scope in which to call the callback
14786      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14787      */
14788     load : function(params, reader, callback, scope, arg){
14789         if(this.fireEvent("beforeload", this, params) !== false){
14790
14791             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14792
14793             var url = this.url;
14794             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14795             if(this.nocache){
14796                 url += "&_dc=" + (new Date().getTime());
14797             }
14798             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14799             var trans = {
14800                 id : transId,
14801                 cb : "stcCallback"+transId,
14802                 scriptId : "stcScript"+transId,
14803                 params : params,
14804                 arg : arg,
14805                 url : url,
14806                 callback : callback,
14807                 scope : scope,
14808                 reader : reader
14809             };
14810             var conn = this;
14811
14812             window[trans.cb] = function(o){
14813                 conn.handleResponse(o, trans);
14814             };
14815
14816             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14817
14818             if(this.autoAbort !== false){
14819                 this.abort();
14820             }
14821
14822             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14823
14824             var script = document.createElement("script");
14825             script.setAttribute("src", url);
14826             script.setAttribute("type", "text/javascript");
14827             script.setAttribute("id", trans.scriptId);
14828             this.head.appendChild(script);
14829
14830             this.trans = trans;
14831         }else{
14832             callback.call(scope||this, null, arg, false);
14833         }
14834     },
14835
14836     // private
14837     isLoading : function(){
14838         return this.trans ? true : false;
14839     },
14840
14841     /**
14842      * Abort the current server request.
14843      */
14844     abort : function(){
14845         if(this.isLoading()){
14846             this.destroyTrans(this.trans);
14847         }
14848     },
14849
14850     // private
14851     destroyTrans : function(trans, isLoaded){
14852         this.head.removeChild(document.getElementById(trans.scriptId));
14853         clearTimeout(trans.timeoutId);
14854         if(isLoaded){
14855             window[trans.cb] = undefined;
14856             try{
14857                 delete window[trans.cb];
14858             }catch(e){}
14859         }else{
14860             // if hasn't been loaded, wait for load to remove it to prevent script error
14861             window[trans.cb] = function(){
14862                 window[trans.cb] = undefined;
14863                 try{
14864                     delete window[trans.cb];
14865                 }catch(e){}
14866             };
14867         }
14868     },
14869
14870     // private
14871     handleResponse : function(o, trans){
14872         this.trans = false;
14873         this.destroyTrans(trans, true);
14874         var result;
14875         try {
14876             result = trans.reader.readRecords(o);
14877         }catch(e){
14878             this.fireEvent("loadexception", this, o, trans.arg, e);
14879             trans.callback.call(trans.scope||window, null, trans.arg, false);
14880             return;
14881         }
14882         this.fireEvent("load", this, o, trans.arg);
14883         trans.callback.call(trans.scope||window, result, trans.arg, true);
14884     },
14885
14886     // private
14887     handleFailure : function(trans){
14888         this.trans = false;
14889         this.destroyTrans(trans, false);
14890         this.fireEvent("loadexception", this, null, trans.arg);
14891         trans.callback.call(trans.scope||window, null, trans.arg, false);
14892     }
14893 });/*
14894  * Based on:
14895  * Ext JS Library 1.1.1
14896  * Copyright(c) 2006-2007, Ext JS, LLC.
14897  *
14898  * Originally Released Under LGPL - original licence link has changed is not relivant.
14899  *
14900  * Fork - LGPL
14901  * <script type="text/javascript">
14902  */
14903
14904 /**
14905  * @class Roo.data.JsonReader
14906  * @extends Roo.data.DataReader
14907  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14908  * based on mappings in a provided Roo.data.Record constructor.
14909  * 
14910  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14911  * in the reply previously. 
14912  * 
14913  * <p>
14914  * Example code:
14915  * <pre><code>
14916 var RecordDef = Roo.data.Record.create([
14917     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14918     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14919 ]);
14920 var myReader = new Roo.data.JsonReader({
14921     totalProperty: "results",    // The property which contains the total dataset size (optional)
14922     root: "rows",                // The property which contains an Array of row objects
14923     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14924 }, RecordDef);
14925 </code></pre>
14926  * <p>
14927  * This would consume a JSON file like this:
14928  * <pre><code>
14929 { 'results': 2, 'rows': [
14930     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14931     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14932 }
14933 </code></pre>
14934  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14935  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14936  * paged from the remote server.
14937  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14938  * @cfg {String} root name of the property which contains the Array of row objects.
14939  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14940  * @cfg {Array} fields Array of field definition objects
14941  * @constructor
14942  * Create a new JsonReader
14943  * @param {Object} meta Metadata configuration options
14944  * @param {Object} recordType Either an Array of field definition objects,
14945  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14946  */
14947 Roo.data.JsonReader = function(meta, recordType){
14948     
14949     meta = meta || {};
14950     // set some defaults:
14951     Roo.applyIf(meta, {
14952         totalProperty: 'total',
14953         successProperty : 'success',
14954         root : 'data',
14955         id : 'id'
14956     });
14957     
14958     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14959 };
14960 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14961     
14962     readerType : 'Json',
14963     
14964     /**
14965      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14966      * Used by Store query builder to append _requestMeta to params.
14967      * 
14968      */
14969     metaFromRemote : false,
14970     /**
14971      * This method is only used by a DataProxy which has retrieved data from a remote server.
14972      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14973      * @return {Object} data A data block which is used by an Roo.data.Store object as
14974      * a cache of Roo.data.Records.
14975      */
14976     read : function(response){
14977         var json = response.responseText;
14978        
14979         var o = /* eval:var:o */ eval("("+json+")");
14980         if(!o) {
14981             throw {message: "JsonReader.read: Json object not found"};
14982         }
14983         
14984         if(o.metaData){
14985             
14986             delete this.ef;
14987             this.metaFromRemote = true;
14988             this.meta = o.metaData;
14989             this.recordType = Roo.data.Record.create(o.metaData.fields);
14990             this.onMetaChange(this.meta, this.recordType, o);
14991         }
14992         return this.readRecords(o);
14993     },
14994
14995     // private function a store will implement
14996     onMetaChange : function(meta, recordType, o){
14997
14998     },
14999
15000     /**
15001          * @ignore
15002          */
15003     simpleAccess: function(obj, subsc) {
15004         return obj[subsc];
15005     },
15006
15007         /**
15008          * @ignore
15009          */
15010     getJsonAccessor: function(){
15011         var re = /[\[\.]/;
15012         return function(expr) {
15013             try {
15014                 return(re.test(expr))
15015                     ? new Function("obj", "return obj." + expr)
15016                     : function(obj){
15017                         return obj[expr];
15018                     };
15019             } catch(e){}
15020             return Roo.emptyFn;
15021         };
15022     }(),
15023
15024     /**
15025      * Create a data block containing Roo.data.Records from an XML document.
15026      * @param {Object} o An object which contains an Array of row objects in the property specified
15027      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15028      * which contains the total size of the dataset.
15029      * @return {Object} data A data block which is used by an Roo.data.Store object as
15030      * a cache of Roo.data.Records.
15031      */
15032     readRecords : function(o){
15033         /**
15034          * After any data loads, the raw JSON data is available for further custom processing.
15035          * @type Object
15036          */
15037         this.o = o;
15038         var s = this.meta, Record = this.recordType,
15039             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15040
15041 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15042         if (!this.ef) {
15043             if(s.totalProperty) {
15044                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15045                 }
15046                 if(s.successProperty) {
15047                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15048                 }
15049                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15050                 if (s.id) {
15051                         var g = this.getJsonAccessor(s.id);
15052                         this.getId = function(rec) {
15053                                 var r = g(rec);  
15054                                 return (r === undefined || r === "") ? null : r;
15055                         };
15056                 } else {
15057                         this.getId = function(){return null;};
15058                 }
15059             this.ef = [];
15060             for(var jj = 0; jj < fl; jj++){
15061                 f = fi[jj];
15062                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15063                 this.ef[jj] = this.getJsonAccessor(map);
15064             }
15065         }
15066
15067         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15068         if(s.totalProperty){
15069             var vt = parseInt(this.getTotal(o), 10);
15070             if(!isNaN(vt)){
15071                 totalRecords = vt;
15072             }
15073         }
15074         if(s.successProperty){
15075             var vs = this.getSuccess(o);
15076             if(vs === false || vs === 'false'){
15077                 success = false;
15078             }
15079         }
15080         var records = [];
15081         for(var i = 0; i < c; i++){
15082                 var n = root[i];
15083             var values = {};
15084             var id = this.getId(n);
15085             for(var j = 0; j < fl; j++){
15086                 f = fi[j];
15087             var v = this.ef[j](n);
15088             if (!f.convert) {
15089                 Roo.log('missing convert for ' + f.name);
15090                 Roo.log(f);
15091                 continue;
15092             }
15093             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15094             }
15095             var record = new Record(values, id);
15096             record.json = n;
15097             records[i] = record;
15098         }
15099         return {
15100             raw : o,
15101             success : success,
15102             records : records,
15103             totalRecords : totalRecords
15104         };
15105     },
15106     // used when loading children.. @see loadDataFromChildren
15107     toLoadData: function(rec)
15108     {
15109         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15110         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15111         return { data : data, total : data.length };
15112         
15113     }
15114 });/*
15115  * Based on:
15116  * Ext JS Library 1.1.1
15117  * Copyright(c) 2006-2007, Ext JS, LLC.
15118  *
15119  * Originally Released Under LGPL - original licence link has changed is not relivant.
15120  *
15121  * Fork - LGPL
15122  * <script type="text/javascript">
15123  */
15124
15125 /**
15126  * @class Roo.data.ArrayReader
15127  * @extends Roo.data.DataReader
15128  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15129  * Each element of that Array represents a row of data fields. The
15130  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15131  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15132  * <p>
15133  * Example code:.
15134  * <pre><code>
15135 var RecordDef = Roo.data.Record.create([
15136     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15137     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15138 ]);
15139 var myReader = new Roo.data.ArrayReader({
15140     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15141 }, RecordDef);
15142 </code></pre>
15143  * <p>
15144  * This would consume an Array like this:
15145  * <pre><code>
15146 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15147   </code></pre>
15148  
15149  * @constructor
15150  * Create a new JsonReader
15151  * @param {Object} meta Metadata configuration options.
15152  * @param {Object|Array} recordType Either an Array of field definition objects
15153  * 
15154  * @cfg {Array} fields Array of field definition objects
15155  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15156  * as specified to {@link Roo.data.Record#create},
15157  * or an {@link Roo.data.Record} object
15158  *
15159  * 
15160  * created using {@link Roo.data.Record#create}.
15161  */
15162 Roo.data.ArrayReader = function(meta, recordType)
15163 {    
15164     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15165 };
15166
15167 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15168     
15169       /**
15170      * Create a data block containing Roo.data.Records from an XML document.
15171      * @param {Object} o An Array of row objects which represents the dataset.
15172      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15173      * a cache of Roo.data.Records.
15174      */
15175     readRecords : function(o)
15176     {
15177         var sid = this.meta ? this.meta.id : null;
15178         var recordType = this.recordType, fields = recordType.prototype.fields;
15179         var records = [];
15180         var root = o;
15181         for(var i = 0; i < root.length; i++){
15182             var n = root[i];
15183             var values = {};
15184             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15185             for(var j = 0, jlen = fields.length; j < jlen; j++){
15186                 var f = fields.items[j];
15187                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15188                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15189                 v = f.convert(v);
15190                 values[f.name] = v;
15191             }
15192             var record = new recordType(values, id);
15193             record.json = n;
15194             records[records.length] = record;
15195         }
15196         return {
15197             records : records,
15198             totalRecords : records.length
15199         };
15200     },
15201     // used when loading children.. @see loadDataFromChildren
15202     toLoadData: function(rec)
15203     {
15204         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15205         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15206         
15207     }
15208     
15209     
15210 });/*
15211  * - LGPL
15212  * * 
15213  */
15214
15215 /**
15216  * @class Roo.bootstrap.ComboBox
15217  * @extends Roo.bootstrap.TriggerField
15218  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15219  * @cfg {Boolean} append (true|false) default false
15220  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15221  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15222  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15223  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15224  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15225  * @cfg {Boolean} animate default true
15226  * @cfg {Boolean} emptyResultText only for touch device
15227  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15228  * @cfg {String} emptyTitle default ''
15229  * @cfg {Number} width fixed with? experimental
15230  * @constructor
15231  * Create a new ComboBox.
15232  * @param {Object} config Configuration options
15233  */
15234 Roo.bootstrap.ComboBox = function(config){
15235     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15236     this.addEvents({
15237         /**
15238          * @event expand
15239          * Fires when the dropdown list is expanded
15240         * @param {Roo.bootstrap.ComboBox} combo This combo box
15241         */
15242         'expand' : true,
15243         /**
15244          * @event collapse
15245          * Fires when the dropdown list is collapsed
15246         * @param {Roo.bootstrap.ComboBox} combo This combo box
15247         */
15248         'collapse' : true,
15249         /**
15250          * @event beforeselect
15251          * Fires before a list item is selected. Return false to cancel the selection.
15252         * @param {Roo.bootstrap.ComboBox} combo This combo box
15253         * @param {Roo.data.Record} record The data record returned from the underlying store
15254         * @param {Number} index The index of the selected item in the dropdown list
15255         */
15256         'beforeselect' : true,
15257         /**
15258          * @event select
15259          * Fires when a list item is selected
15260         * @param {Roo.bootstrap.ComboBox} combo This combo box
15261         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15262         * @param {Number} index The index of the selected item in the dropdown list
15263         */
15264         'select' : true,
15265         /**
15266          * @event beforequery
15267          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15268          * The event object passed has these properties:
15269         * @param {Roo.bootstrap.ComboBox} combo This combo box
15270         * @param {String} query The query
15271         * @param {Boolean} forceAll true to force "all" query
15272         * @param {Boolean} cancel true to cancel the query
15273         * @param {Object} e The query event object
15274         */
15275         'beforequery': true,
15276          /**
15277          * @event add
15278          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15279         * @param {Roo.bootstrap.ComboBox} combo This combo box
15280         */
15281         'add' : true,
15282         /**
15283          * @event edit
15284          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15285         * @param {Roo.bootstrap.ComboBox} combo This combo box
15286         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15287         */
15288         'edit' : true,
15289         /**
15290          * @event remove
15291          * Fires when the remove value from the combobox array
15292         * @param {Roo.bootstrap.ComboBox} combo This combo box
15293         */
15294         'remove' : true,
15295         /**
15296          * @event afterremove
15297          * Fires when the remove value from the combobox array
15298         * @param {Roo.bootstrap.ComboBox} combo This combo box
15299         */
15300         'afterremove' : true,
15301         /**
15302          * @event specialfilter
15303          * Fires when specialfilter
15304             * @param {Roo.bootstrap.ComboBox} combo This combo box
15305             */
15306         'specialfilter' : true,
15307         /**
15308          * @event tick
15309          * Fires when tick the element
15310             * @param {Roo.bootstrap.ComboBox} combo This combo box
15311             */
15312         'tick' : true,
15313         /**
15314          * @event touchviewdisplay
15315          * Fires when touch view require special display (default is using displayField)
15316             * @param {Roo.bootstrap.ComboBox} combo This combo box
15317             * @param {Object} cfg set html .
15318             */
15319         'touchviewdisplay' : true
15320         
15321     });
15322     
15323     this.item = [];
15324     this.tickItems = [];
15325     
15326     this.selectedIndex = -1;
15327     if(this.mode == 'local'){
15328         if(config.queryDelay === undefined){
15329             this.queryDelay = 10;
15330         }
15331         if(config.minChars === undefined){
15332             this.minChars = 0;
15333         }
15334     }
15335 };
15336
15337 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15338      
15339     /**
15340      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15341      * rendering into an Roo.Editor, defaults to false)
15342      */
15343     /**
15344      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15345      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15346      */
15347     /**
15348      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15349      */
15350     /**
15351      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15352      * the dropdown list (defaults to undefined, with no header element)
15353      */
15354
15355      /**
15356      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15357      */
15358      
15359      /**
15360      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15361      */
15362     listWidth: undefined,
15363     /**
15364      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15365      * mode = 'remote' or 'text' if mode = 'local')
15366      */
15367     displayField: undefined,
15368     
15369     /**
15370      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15371      * mode = 'remote' or 'value' if mode = 'local'). 
15372      * Note: use of a valueField requires the user make a selection
15373      * in order for a value to be mapped.
15374      */
15375     valueField: undefined,
15376     /**
15377      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15378      */
15379     modalTitle : '',
15380     
15381     /**
15382      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15383      * field's data value (defaults to the underlying DOM element's name)
15384      */
15385     hiddenName: undefined,
15386     /**
15387      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15388      */
15389     listClass: '',
15390     /**
15391      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15392      */
15393     selectedClass: 'active',
15394     
15395     /**
15396      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15397      */
15398     shadow:'sides',
15399     /**
15400      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15401      * anchor positions (defaults to 'tl-bl')
15402      */
15403     listAlign: 'tl-bl?',
15404     /**
15405      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15406      */
15407     maxHeight: 300,
15408     /**
15409      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15410      * query specified by the allQuery config option (defaults to 'query')
15411      */
15412     triggerAction: 'query',
15413     /**
15414      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15415      * (defaults to 4, does not apply if editable = false)
15416      */
15417     minChars : 4,
15418     /**
15419      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15420      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15421      */
15422     typeAhead: false,
15423     /**
15424      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15425      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15426      */
15427     queryDelay: 500,
15428     /**
15429      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15430      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15431      */
15432     pageSize: 0,
15433     /**
15434      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15435      * when editable = true (defaults to false)
15436      */
15437     selectOnFocus:false,
15438     /**
15439      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15440      */
15441     queryParam: 'query',
15442     /**
15443      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15444      * when mode = 'remote' (defaults to 'Loading...')
15445      */
15446     loadingText: 'Loading...',
15447     /**
15448      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15449      */
15450     resizable: false,
15451     /**
15452      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15453      */
15454     handleHeight : 8,
15455     /**
15456      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15457      * traditional select (defaults to true)
15458      */
15459     editable: true,
15460     /**
15461      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15462      */
15463     allQuery: '',
15464     /**
15465      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15466      */
15467     mode: 'remote',
15468     /**
15469      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15470      * listWidth has a higher value)
15471      */
15472     minListWidth : 70,
15473     /**
15474      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15475      * allow the user to set arbitrary text into the field (defaults to false)
15476      */
15477     forceSelection:false,
15478     /**
15479      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15480      * if typeAhead = true (defaults to 250)
15481      */
15482     typeAheadDelay : 250,
15483     /**
15484      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15485      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15486      */
15487     valueNotFoundText : undefined,
15488     /**
15489      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15490      */
15491     blockFocus : false,
15492     
15493     /**
15494      * @cfg {Boolean} disableClear Disable showing of clear button.
15495      */
15496     disableClear : false,
15497     /**
15498      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15499      */
15500     alwaysQuery : false,
15501     
15502     /**
15503      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15504      */
15505     multiple : false,
15506     
15507     /**
15508      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15509      */
15510     invalidClass : "has-warning",
15511     
15512     /**
15513      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15514      */
15515     validClass : "has-success",
15516     
15517     /**
15518      * @cfg {Boolean} specialFilter (true|false) special filter default false
15519      */
15520     specialFilter : false,
15521     
15522     /**
15523      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15524      */
15525     mobileTouchView : true,
15526     
15527     /**
15528      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15529      */
15530     useNativeIOS : false,
15531     
15532     /**
15533      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15534      */
15535     mobile_restrict_height : false,
15536     
15537     ios_options : false,
15538     
15539     //private
15540     addicon : false,
15541     editicon: false,
15542     
15543     page: 0,
15544     hasQuery: false,
15545     append: false,
15546     loadNext: false,
15547     autoFocus : true,
15548     tickable : false,
15549     btnPosition : 'right',
15550     triggerList : true,
15551     showToggleBtn : true,
15552     animate : true,
15553     emptyResultText: 'Empty',
15554     triggerText : 'Select',
15555     emptyTitle : '',
15556     width : false,
15557     
15558     // element that contains real text value.. (when hidden is used..)
15559     
15560     getAutoCreate : function()
15561     {   
15562         var cfg = false;
15563         //render
15564         /*
15565          * Render classic select for iso
15566          */
15567         
15568         if(Roo.isIOS && this.useNativeIOS){
15569             cfg = this.getAutoCreateNativeIOS();
15570             return cfg;
15571         }
15572         
15573         /*
15574          * Touch Devices
15575          */
15576         
15577         if(Roo.isTouch && this.mobileTouchView){
15578             cfg = this.getAutoCreateTouchView();
15579             return cfg;;
15580         }
15581         
15582         /*
15583          *  Normal ComboBox
15584          */
15585         if(!this.tickable){
15586             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15587             return cfg;
15588         }
15589         
15590         /*
15591          *  ComboBox with tickable selections
15592          */
15593              
15594         var align = this.labelAlign || this.parentLabelAlign();
15595         
15596         cfg = {
15597             cls : 'form-group roo-combobox-tickable' //input-group
15598         };
15599         
15600         var btn_text_select = '';
15601         var btn_text_done = '';
15602         var btn_text_cancel = '';
15603         
15604         if (this.btn_text_show) {
15605             btn_text_select = 'Select';
15606             btn_text_done = 'Done';
15607             btn_text_cancel = 'Cancel'; 
15608         }
15609         
15610         var buttons = {
15611             tag : 'div',
15612             cls : 'tickable-buttons',
15613             cn : [
15614                 {
15615                     tag : 'button',
15616                     type : 'button',
15617                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15618                     //html : this.triggerText
15619                     html: btn_text_select
15620                 },
15621                 {
15622                     tag : 'button',
15623                     type : 'button',
15624                     name : 'ok',
15625                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15626                     //html : 'Done'
15627                     html: btn_text_done
15628                 },
15629                 {
15630                     tag : 'button',
15631                     type : 'button',
15632                     name : 'cancel',
15633                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15634                     //html : 'Cancel'
15635                     html: btn_text_cancel
15636                 }
15637             ]
15638         };
15639         
15640         if(this.editable){
15641             buttons.cn.unshift({
15642                 tag: 'input',
15643                 cls: 'roo-select2-search-field-input'
15644             });
15645         }
15646         
15647         var _this = this;
15648         
15649         Roo.each(buttons.cn, function(c){
15650             if (_this.size) {
15651                 c.cls += ' btn-' + _this.size;
15652             }
15653
15654             if (_this.disabled) {
15655                 c.disabled = true;
15656             }
15657         });
15658         
15659         var box = {
15660             tag: 'div',
15661             style : 'display: contents',
15662             cn: [
15663                 {
15664                     tag: 'input',
15665                     type : 'hidden',
15666                     cls: 'form-hidden-field'
15667                 },
15668                 {
15669                     tag: 'ul',
15670                     cls: 'roo-select2-choices',
15671                     cn:[
15672                         {
15673                             tag: 'li',
15674                             cls: 'roo-select2-search-field',
15675                             cn: [
15676                                 buttons
15677                             ]
15678                         }
15679                     ]
15680                 }
15681             ]
15682         };
15683         
15684         var combobox = {
15685             cls: 'roo-select2-container input-group roo-select2-container-multi',
15686             cn: [
15687                 
15688                 box
15689 //                {
15690 //                    tag: 'ul',
15691 //                    cls: 'typeahead typeahead-long dropdown-menu',
15692 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15693 //                }
15694             ]
15695         };
15696         
15697         if(this.hasFeedback && !this.allowBlank){
15698             
15699             var feedback = {
15700                 tag: 'span',
15701                 cls: 'glyphicon form-control-feedback'
15702             };
15703
15704             combobox.cn.push(feedback);
15705         }
15706         
15707         
15708         
15709         var indicator = {
15710             tag : 'i',
15711             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15712             tooltip : 'This field is required'
15713         };
15714         if (Roo.bootstrap.version == 4) {
15715             indicator = {
15716                 tag : 'i',
15717                 style : 'display:none'
15718             };
15719         }
15720         if (align ==='left' && this.fieldLabel.length) {
15721             
15722             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15723             
15724             cfg.cn = [
15725                 indicator,
15726                 {
15727                     tag: 'label',
15728                     'for' :  id,
15729                     cls : 'control-label col-form-label',
15730                     html : this.fieldLabel
15731
15732                 },
15733                 {
15734                     cls : "", 
15735                     cn: [
15736                         combobox
15737                     ]
15738                 }
15739
15740             ];
15741             
15742             var labelCfg = cfg.cn[1];
15743             var contentCfg = cfg.cn[2];
15744             
15745
15746             if(this.indicatorpos == 'right'){
15747                 
15748                 cfg.cn = [
15749                     {
15750                         tag: 'label',
15751                         'for' :  id,
15752                         cls : 'control-label col-form-label',
15753                         cn : [
15754                             {
15755                                 tag : 'span',
15756                                 html : this.fieldLabel
15757                             },
15758                             indicator
15759                         ]
15760                     },
15761                     {
15762                         cls : "",
15763                         cn: [
15764                             combobox
15765                         ]
15766                     }
15767
15768                 ];
15769                 
15770                 
15771                 
15772                 labelCfg = cfg.cn[0];
15773                 contentCfg = cfg.cn[1];
15774             
15775             }
15776             
15777             if(this.labelWidth > 12){
15778                 labelCfg.style = "width: " + this.labelWidth + 'px';
15779             }
15780             if(this.width * 1 > 0){
15781                 contentCfg.style = "width: " + this.width + 'px';
15782             }
15783             if(this.labelWidth < 13 && this.labelmd == 0){
15784                 this.labelmd = this.labelWidth;
15785             }
15786             
15787             if(this.labellg > 0){
15788                 labelCfg.cls += ' col-lg-' + this.labellg;
15789                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15790             }
15791             
15792             if(this.labelmd > 0){
15793                 labelCfg.cls += ' col-md-' + this.labelmd;
15794                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15795             }
15796             
15797             if(this.labelsm > 0){
15798                 labelCfg.cls += ' col-sm-' + this.labelsm;
15799                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15800             }
15801             
15802             if(this.labelxs > 0){
15803                 labelCfg.cls += ' col-xs-' + this.labelxs;
15804                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15805             }
15806                 
15807                 
15808         } else if ( this.fieldLabel.length) {
15809 //                Roo.log(" label");
15810                  cfg.cn = [
15811                    indicator,
15812                     {
15813                         tag: 'label',
15814                         //cls : 'input-group-addon',
15815                         html : this.fieldLabel
15816                     },
15817                     combobox
15818                 ];
15819                 
15820                 if(this.indicatorpos == 'right'){
15821                     cfg.cn = [
15822                         {
15823                             tag: 'label',
15824                             //cls : 'input-group-addon',
15825                             html : this.fieldLabel
15826                         },
15827                         indicator,
15828                         combobox
15829                     ];
15830                     
15831                 }
15832
15833         } else {
15834             
15835 //                Roo.log(" no label && no align");
15836                 cfg = combobox
15837                      
15838                 
15839         }
15840          
15841         var settings=this;
15842         ['xs','sm','md','lg'].map(function(size){
15843             if (settings[size]) {
15844                 cfg.cls += ' col-' + size + '-' + settings[size];
15845             }
15846         });
15847         
15848         return cfg;
15849         
15850     },
15851     
15852     _initEventsCalled : false,
15853     
15854     // private
15855     initEvents: function()
15856     {   
15857         if (this._initEventsCalled) { // as we call render... prevent looping...
15858             return;
15859         }
15860         this._initEventsCalled = true;
15861         
15862         if (!this.store) {
15863             throw "can not find store for combo";
15864         }
15865         
15866         this.indicator = this.indicatorEl();
15867         
15868         this.store = Roo.factory(this.store, Roo.data);
15869         this.store.parent = this;
15870         
15871         // if we are building from html. then this element is so complex, that we can not really
15872         // use the rendered HTML.
15873         // so we have to trash and replace the previous code.
15874         if (Roo.XComponent.build_from_html) {
15875             // remove this element....
15876             var e = this.el.dom, k=0;
15877             while (e ) { e = e.previousSibling;  ++k;}
15878
15879             this.el.remove();
15880             
15881             this.el=false;
15882             this.rendered = false;
15883             
15884             this.render(this.parent().getChildContainer(true), k);
15885         }
15886         
15887         if(Roo.isIOS && this.useNativeIOS){
15888             this.initIOSView();
15889             return;
15890         }
15891         
15892         /*
15893          * Touch Devices
15894          */
15895         
15896         if(Roo.isTouch && this.mobileTouchView){
15897             this.initTouchView();
15898             return;
15899         }
15900         
15901         if(this.tickable){
15902             this.initTickableEvents();
15903             return;
15904         }
15905         
15906         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15907         
15908         if(this.hiddenName){
15909             
15910             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15911             
15912             this.hiddenField.dom.value =
15913                 this.hiddenValue !== undefined ? this.hiddenValue :
15914                 this.value !== undefined ? this.value : '';
15915
15916             // prevent input submission
15917             this.el.dom.removeAttribute('name');
15918             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15919              
15920              
15921         }
15922         //if(Roo.isGecko){
15923         //    this.el.dom.setAttribute('autocomplete', 'off');
15924         //}
15925         
15926         var cls = 'x-combo-list';
15927         
15928         //this.list = new Roo.Layer({
15929         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15930         //});
15931         
15932         var _this = this;
15933         
15934         (function(){
15935             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15936             _this.list.setWidth(lw);
15937         }).defer(100);
15938         
15939         this.list.on('mouseover', this.onViewOver, this);
15940         this.list.on('mousemove', this.onViewMove, this);
15941         this.list.on('scroll', this.onViewScroll, this);
15942         
15943         /*
15944         this.list.swallowEvent('mousewheel');
15945         this.assetHeight = 0;
15946
15947         if(this.title){
15948             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15949             this.assetHeight += this.header.getHeight();
15950         }
15951
15952         this.innerList = this.list.createChild({cls:cls+'-inner'});
15953         this.innerList.on('mouseover', this.onViewOver, this);
15954         this.innerList.on('mousemove', this.onViewMove, this);
15955         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15956         
15957         if(this.allowBlank && !this.pageSize && !this.disableClear){
15958             this.footer = this.list.createChild({cls:cls+'-ft'});
15959             this.pageTb = new Roo.Toolbar(this.footer);
15960            
15961         }
15962         if(this.pageSize){
15963             this.footer = this.list.createChild({cls:cls+'-ft'});
15964             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15965                     {pageSize: this.pageSize});
15966             
15967         }
15968         
15969         if (this.pageTb && this.allowBlank && !this.disableClear) {
15970             var _this = this;
15971             this.pageTb.add(new Roo.Toolbar.Fill(), {
15972                 cls: 'x-btn-icon x-btn-clear',
15973                 text: '&#160;',
15974                 handler: function()
15975                 {
15976                     _this.collapse();
15977                     _this.clearValue();
15978                     _this.onSelect(false, -1);
15979                 }
15980             });
15981         }
15982         if (this.footer) {
15983             this.assetHeight += this.footer.getHeight();
15984         }
15985         */
15986             
15987         if(!this.tpl){
15988             this.tpl = Roo.bootstrap.version == 4 ?
15989                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15990                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15991         }
15992
15993         this.view = new Roo.View(this.list, this.tpl, {
15994             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15995         });
15996         //this.view.wrapEl.setDisplayed(false);
15997         this.view.on('click', this.onViewClick, this);
15998         
15999         
16000         this.store.on('beforeload', this.onBeforeLoad, this);
16001         this.store.on('load', this.onLoad, this);
16002         this.store.on('loadexception', this.onLoadException, this);
16003         /*
16004         if(this.resizable){
16005             this.resizer = new Roo.Resizable(this.list,  {
16006                pinned:true, handles:'se'
16007             });
16008             this.resizer.on('resize', function(r, w, h){
16009                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16010                 this.listWidth = w;
16011                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16012                 this.restrictHeight();
16013             }, this);
16014             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16015         }
16016         */
16017         if(!this.editable){
16018             this.editable = true;
16019             this.setEditable(false);
16020         }
16021         
16022         /*
16023         
16024         if (typeof(this.events.add.listeners) != 'undefined') {
16025             
16026             this.addicon = this.wrap.createChild(
16027                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16028        
16029             this.addicon.on('click', function(e) {
16030                 this.fireEvent('add', this);
16031             }, this);
16032         }
16033         if (typeof(this.events.edit.listeners) != 'undefined') {
16034             
16035             this.editicon = this.wrap.createChild(
16036                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16037             if (this.addicon) {
16038                 this.editicon.setStyle('margin-left', '40px');
16039             }
16040             this.editicon.on('click', function(e) {
16041                 
16042                 // we fire even  if inothing is selected..
16043                 this.fireEvent('edit', this, this.lastData );
16044                 
16045             }, this);
16046         }
16047         */
16048         
16049         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16050             "up" : function(e){
16051                 this.inKeyMode = true;
16052                 this.selectPrev();
16053             },
16054
16055             "down" : function(e){
16056                 if(!this.isExpanded()){
16057                     this.onTriggerClick();
16058                 }else{
16059                     this.inKeyMode = true;
16060                     this.selectNext();
16061                 }
16062             },
16063
16064             "enter" : function(e){
16065 //                this.onViewClick();
16066                 //return true;
16067                 this.collapse();
16068                 
16069                 if(this.fireEvent("specialkey", this, e)){
16070                     this.onViewClick(false);
16071                 }
16072                 
16073                 return true;
16074             },
16075
16076             "esc" : function(e){
16077                 this.collapse();
16078             },
16079
16080             "tab" : function(e){
16081                 this.collapse();
16082                 
16083                 if(this.fireEvent("specialkey", this, e)){
16084                     this.onViewClick(false);
16085                 }
16086                 
16087                 return true;
16088             },
16089
16090             scope : this,
16091
16092             doRelay : function(foo, bar, hname){
16093                 if(hname == 'down' || this.scope.isExpanded()){
16094                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16095                 }
16096                 return true;
16097             },
16098
16099             forceKeyDown: true
16100         });
16101         
16102         
16103         this.queryDelay = Math.max(this.queryDelay || 10,
16104                 this.mode == 'local' ? 10 : 250);
16105         
16106         
16107         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16108         
16109         if(this.typeAhead){
16110             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16111         }
16112         if(this.editable !== false){
16113             this.inputEl().on("keyup", this.onKeyUp, this);
16114         }
16115         if(this.forceSelection){
16116             this.inputEl().on('blur', this.doForce, this);
16117         }
16118         
16119         if(this.multiple){
16120             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16121             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16122         }
16123     },
16124     
16125     initTickableEvents: function()
16126     {   
16127         this.createList();
16128         
16129         if(this.hiddenName){
16130             
16131             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16132             
16133             this.hiddenField.dom.value =
16134                 this.hiddenValue !== undefined ? this.hiddenValue :
16135                 this.value !== undefined ? this.value : '';
16136
16137             // prevent input submission
16138             this.el.dom.removeAttribute('name');
16139             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16140              
16141              
16142         }
16143         
16144 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16145         
16146         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16147         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16148         if(this.triggerList){
16149             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16150         }
16151          
16152         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16153         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16154         
16155         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16156         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16157         
16158         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16159         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16160         
16161         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16162         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16163         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16164         
16165         this.okBtn.hide();
16166         this.cancelBtn.hide();
16167         
16168         var _this = this;
16169         
16170         (function(){
16171             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16172             _this.list.setWidth(lw);
16173         }).defer(100);
16174         
16175         this.list.on('mouseover', this.onViewOver, this);
16176         this.list.on('mousemove', this.onViewMove, this);
16177         
16178         this.list.on('scroll', this.onViewScroll, this);
16179         
16180         if(!this.tpl){
16181             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16182                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16183         }
16184
16185         this.view = new Roo.View(this.list, this.tpl, {
16186             singleSelect:true,
16187             tickable:true,
16188             parent:this,
16189             store: this.store,
16190             selectedClass: this.selectedClass
16191         });
16192         
16193         //this.view.wrapEl.setDisplayed(false);
16194         this.view.on('click', this.onViewClick, this);
16195         
16196         
16197         
16198         this.store.on('beforeload', this.onBeforeLoad, this);
16199         this.store.on('load', this.onLoad, this);
16200         this.store.on('loadexception', this.onLoadException, this);
16201         
16202         if(this.editable){
16203             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16204                 "up" : function(e){
16205                     this.inKeyMode = true;
16206                     this.selectPrev();
16207                 },
16208
16209                 "down" : function(e){
16210                     this.inKeyMode = true;
16211                     this.selectNext();
16212                 },
16213
16214                 "enter" : function(e){
16215                     if(this.fireEvent("specialkey", this, e)){
16216                         this.onViewClick(false);
16217                     }
16218                     
16219                     return true;
16220                 },
16221
16222                 "esc" : function(e){
16223                     this.onTickableFooterButtonClick(e, false, false);
16224                 },
16225
16226                 "tab" : function(e){
16227                     this.fireEvent("specialkey", this, e);
16228                     
16229                     this.onTickableFooterButtonClick(e, false, false);
16230                     
16231                     return true;
16232                 },
16233
16234                 scope : this,
16235
16236                 doRelay : function(e, fn, key){
16237                     if(this.scope.isExpanded()){
16238                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16239                     }
16240                     return true;
16241                 },
16242
16243                 forceKeyDown: true
16244             });
16245         }
16246         
16247         this.queryDelay = Math.max(this.queryDelay || 10,
16248                 this.mode == 'local' ? 10 : 250);
16249         
16250         
16251         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16252         
16253         if(this.typeAhead){
16254             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16255         }
16256         
16257         if(this.editable !== false){
16258             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16259         }
16260         
16261         this.indicator = this.indicatorEl();
16262         
16263         if(this.indicator){
16264             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16265             this.indicator.hide();
16266         }
16267         
16268     },
16269
16270     onDestroy : function(){
16271         if(this.view){
16272             this.view.setStore(null);
16273             this.view.el.removeAllListeners();
16274             this.view.el.remove();
16275             this.view.purgeListeners();
16276         }
16277         if(this.list){
16278             this.list.dom.innerHTML  = '';
16279         }
16280         
16281         if(this.store){
16282             this.store.un('beforeload', this.onBeforeLoad, this);
16283             this.store.un('load', this.onLoad, this);
16284             this.store.un('loadexception', this.onLoadException, this);
16285         }
16286         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16287     },
16288
16289     // private
16290     fireKey : function(e){
16291         if(e.isNavKeyPress() && !this.list.isVisible()){
16292             this.fireEvent("specialkey", this, e);
16293         }
16294     },
16295
16296     // private
16297     onResize: function(w, h)
16298     {
16299         
16300         
16301 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16302 //        
16303 //        if(typeof w != 'number'){
16304 //            // we do not handle it!?!?
16305 //            return;
16306 //        }
16307 //        var tw = this.trigger.getWidth();
16308 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16309 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16310 //        var x = w - tw;
16311 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16312 //            
16313 //        //this.trigger.setStyle('left', x+'px');
16314 //        
16315 //        if(this.list && this.listWidth === undefined){
16316 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16317 //            this.list.setWidth(lw);
16318 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16319 //        }
16320         
16321     
16322         
16323     },
16324
16325     /**
16326      * Allow or prevent the user from directly editing the field text.  If false is passed,
16327      * the user will only be able to select from the items defined in the dropdown list.  This method
16328      * is the runtime equivalent of setting the 'editable' config option at config time.
16329      * @param {Boolean} value True to allow the user to directly edit the field text
16330      */
16331     setEditable : function(value){
16332         if(value == this.editable){
16333             return;
16334         }
16335         this.editable = value;
16336         if(!value){
16337             this.inputEl().dom.setAttribute('readOnly', true);
16338             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16339             this.inputEl().addClass('x-combo-noedit');
16340         }else{
16341             this.inputEl().dom.setAttribute('readOnly', false);
16342             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16343             this.inputEl().removeClass('x-combo-noedit');
16344         }
16345     },
16346
16347     // private
16348     
16349     onBeforeLoad : function(combo,opts){
16350         if(!this.hasFocus){
16351             return;
16352         }
16353          if (!opts.add) {
16354             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16355          }
16356         this.restrictHeight();
16357         this.selectedIndex = -1;
16358     },
16359
16360     // private
16361     onLoad : function(){
16362         
16363         this.hasQuery = false;
16364         
16365         if(!this.hasFocus){
16366             return;
16367         }
16368         
16369         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16370             this.loading.hide();
16371         }
16372         
16373         if(this.store.getCount() > 0){
16374             
16375             this.expand();
16376             this.restrictHeight();
16377             if(this.lastQuery == this.allQuery){
16378                 if(this.editable && !this.tickable){
16379                     this.inputEl().dom.select();
16380                 }
16381                 
16382                 if(
16383                     !this.selectByValue(this.value, true) &&
16384                     this.autoFocus && 
16385                     (
16386                         !this.store.lastOptions ||
16387                         typeof(this.store.lastOptions.add) == 'undefined' || 
16388                         this.store.lastOptions.add != true
16389                     )
16390                 ){
16391                     this.select(0, true);
16392                 }
16393             }else{
16394                 if(this.autoFocus){
16395                     this.selectNext();
16396                 }
16397                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16398                     this.taTask.delay(this.typeAheadDelay);
16399                 }
16400             }
16401         }else{
16402             this.onEmptyResults();
16403         }
16404         
16405         //this.el.focus();
16406     },
16407     // private
16408     onLoadException : function()
16409     {
16410         this.hasQuery = false;
16411         
16412         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16413             this.loading.hide();
16414         }
16415         
16416         if(this.tickable && this.editable){
16417             return;
16418         }
16419         
16420         this.collapse();
16421         // only causes errors at present
16422         //Roo.log(this.store.reader.jsonData);
16423         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16424             // fixme
16425             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16426         //}
16427         
16428         
16429     },
16430     // private
16431     onTypeAhead : function(){
16432         if(this.store.getCount() > 0){
16433             var r = this.store.getAt(0);
16434             var newValue = r.data[this.displayField];
16435             var len = newValue.length;
16436             var selStart = this.getRawValue().length;
16437             
16438             if(selStart != len){
16439                 this.setRawValue(newValue);
16440                 this.selectText(selStart, newValue.length);
16441             }
16442         }
16443     },
16444
16445     // private
16446     onSelect : function(record, index){
16447         
16448         if(this.fireEvent('beforeselect', this, record, index) !== false){
16449         
16450             this.setFromData(index > -1 ? record.data : false);
16451             
16452             this.collapse();
16453             this.fireEvent('select', this, record, index);
16454         }
16455     },
16456
16457     /**
16458      * Returns the currently selected field value or empty string if no value is set.
16459      * @return {String} value The selected value
16460      */
16461     getValue : function()
16462     {
16463         if(Roo.isIOS && this.useNativeIOS){
16464             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16465         }
16466         
16467         if(this.multiple){
16468             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16469         }
16470         
16471         if(this.valueField){
16472             return typeof this.value != 'undefined' ? this.value : '';
16473         }else{
16474             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16475         }
16476     },
16477     
16478     getRawValue : function()
16479     {
16480         if(Roo.isIOS && this.useNativeIOS){
16481             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16482         }
16483         
16484         var v = this.inputEl().getValue();
16485         
16486         return v;
16487     },
16488
16489     /**
16490      * Clears any text/value currently set in the field
16491      */
16492     clearValue : function(){
16493         
16494         if(this.hiddenField){
16495             this.hiddenField.dom.value = '';
16496         }
16497         this.value = '';
16498         this.setRawValue('');
16499         this.lastSelectionText = '';
16500         this.lastData = false;
16501         
16502         var close = this.closeTriggerEl();
16503         
16504         if(close){
16505             close.hide();
16506         }
16507         
16508         this.validate();
16509         
16510     },
16511
16512     /**
16513      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16514      * will be displayed in the field.  If the value does not match the data value of an existing item,
16515      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16516      * Otherwise the field will be blank (although the value will still be set).
16517      * @param {String} value The value to match
16518      */
16519     setValue : function(v)
16520     {
16521         if(Roo.isIOS && this.useNativeIOS){
16522             this.setIOSValue(v);
16523             return;
16524         }
16525         
16526         if(this.multiple){
16527             this.syncValue();
16528             return;
16529         }
16530         
16531         var text = v;
16532         if(this.valueField){
16533             var r = this.findRecord(this.valueField, v);
16534             if(r){
16535                 text = r.data[this.displayField];
16536             }else if(this.valueNotFoundText !== undefined){
16537                 text = this.valueNotFoundText;
16538             }
16539         }
16540         this.lastSelectionText = text;
16541         if(this.hiddenField){
16542             this.hiddenField.dom.value = v;
16543         }
16544         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16545         this.value = v;
16546         
16547         var close = this.closeTriggerEl();
16548         
16549         if(close){
16550             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16551         }
16552         
16553         this.validate();
16554     },
16555     /**
16556      * @property {Object} the last set data for the element
16557      */
16558     
16559     lastData : false,
16560     /**
16561      * Sets the value of the field based on a object which is related to the record format for the store.
16562      * @param {Object} value the value to set as. or false on reset?
16563      */
16564     setFromData : function(o){
16565         
16566         if(this.multiple){
16567             this.addItem(o);
16568             return;
16569         }
16570             
16571         var dv = ''; // display value
16572         var vv = ''; // value value..
16573         this.lastData = o;
16574         if (this.displayField) {
16575             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16576         } else {
16577             // this is an error condition!!!
16578             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16579         }
16580         
16581         if(this.valueField){
16582             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16583         }
16584         
16585         var close = this.closeTriggerEl();
16586         
16587         if(close){
16588             if(dv.length || vv * 1 > 0){
16589                 close.show() ;
16590                 this.blockFocus=true;
16591             } else {
16592                 close.hide();
16593             }             
16594         }
16595         
16596         if(this.hiddenField){
16597             this.hiddenField.dom.value = vv;
16598             
16599             this.lastSelectionText = dv;
16600             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16601             this.value = vv;
16602             return;
16603         }
16604         // no hidden field.. - we store the value in 'value', but still display
16605         // display field!!!!
16606         this.lastSelectionText = dv;
16607         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16608         this.value = vv;
16609         
16610         
16611         
16612     },
16613     // private
16614     reset : function(){
16615         // overridden so that last data is reset..
16616         
16617         if(this.multiple){
16618             this.clearItem();
16619             return;
16620         }
16621         
16622         this.setValue(this.originalValue);
16623         //this.clearInvalid();
16624         this.lastData = false;
16625         if (this.view) {
16626             this.view.clearSelections();
16627         }
16628         
16629         this.validate();
16630     },
16631     // private
16632     findRecord : function(prop, value){
16633         var record;
16634         if(this.store.getCount() > 0){
16635             this.store.each(function(r){
16636                 if(r.data[prop] == value){
16637                     record = r;
16638                     return false;
16639                 }
16640                 return true;
16641             });
16642         }
16643         return record;
16644     },
16645     
16646     getName: function()
16647     {
16648         // returns hidden if it's set..
16649         if (!this.rendered) {return ''};
16650         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16651         
16652     },
16653     // private
16654     onViewMove : function(e, t){
16655         this.inKeyMode = false;
16656     },
16657
16658     // private
16659     onViewOver : function(e, t){
16660         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16661             return;
16662         }
16663         var item = this.view.findItemFromChild(t);
16664         
16665         if(item){
16666             var index = this.view.indexOf(item);
16667             this.select(index, false);
16668         }
16669     },
16670
16671     // private
16672     onViewClick : function(view, doFocus, el, e)
16673     {
16674         var index = this.view.getSelectedIndexes()[0];
16675         
16676         var r = this.store.getAt(index);
16677         
16678         if(this.tickable){
16679             
16680             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16681                 return;
16682             }
16683             
16684             var rm = false;
16685             var _this = this;
16686             
16687             Roo.each(this.tickItems, function(v,k){
16688                 
16689                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16690                     Roo.log(v);
16691                     _this.tickItems.splice(k, 1);
16692                     
16693                     if(typeof(e) == 'undefined' && view == false){
16694                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16695                     }
16696                     
16697                     rm = true;
16698                     return;
16699                 }
16700             });
16701             
16702             if(rm){
16703                 return;
16704             }
16705             
16706             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16707                 this.tickItems.push(r.data);
16708             }
16709             
16710             if(typeof(e) == 'undefined' && view == false){
16711                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16712             }
16713                     
16714             return;
16715         }
16716         
16717         if(r){
16718             this.onSelect(r, index);
16719         }
16720         if(doFocus !== false && !this.blockFocus){
16721             this.inputEl().focus();
16722         }
16723     },
16724
16725     // private
16726     restrictHeight : function(){
16727         //this.innerList.dom.style.height = '';
16728         //var inner = this.innerList.dom;
16729         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16730         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16731         //this.list.beginUpdate();
16732         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16733         this.list.alignTo(this.inputEl(), this.listAlign);
16734         this.list.alignTo(this.inputEl(), this.listAlign);
16735         //this.list.endUpdate();
16736     },
16737
16738     // private
16739     onEmptyResults : function(){
16740         
16741         if(this.tickable && this.editable){
16742             this.hasFocus = false;
16743             this.restrictHeight();
16744             return;
16745         }
16746         
16747         this.collapse();
16748     },
16749
16750     /**
16751      * Returns true if the dropdown list is expanded, else false.
16752      */
16753     isExpanded : function(){
16754         return this.list.isVisible();
16755     },
16756
16757     /**
16758      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16759      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16760      * @param {String} value The data value of the item to select
16761      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16762      * selected item if it is not currently in view (defaults to true)
16763      * @return {Boolean} True if the value matched an item in the list, else false
16764      */
16765     selectByValue : function(v, scrollIntoView){
16766         if(v !== undefined && v !== null){
16767             var r = this.findRecord(this.valueField || this.displayField, v);
16768             if(r){
16769                 this.select(this.store.indexOf(r), scrollIntoView);
16770                 return true;
16771             }
16772         }
16773         return false;
16774     },
16775
16776     /**
16777      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16778      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16779      * @param {Number} index The zero-based index of the list item to select
16780      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16781      * selected item if it is not currently in view (defaults to true)
16782      */
16783     select : function(index, scrollIntoView){
16784         this.selectedIndex = index;
16785         this.view.select(index);
16786         if(scrollIntoView !== false){
16787             var el = this.view.getNode(index);
16788             /*
16789              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16790              */
16791             if(el){
16792                 this.list.scrollChildIntoView(el, false);
16793             }
16794         }
16795     },
16796
16797     // private
16798     selectNext : function(){
16799         var ct = this.store.getCount();
16800         if(ct > 0){
16801             if(this.selectedIndex == -1){
16802                 this.select(0);
16803             }else if(this.selectedIndex < ct-1){
16804                 this.select(this.selectedIndex+1);
16805             }
16806         }
16807     },
16808
16809     // private
16810     selectPrev : function(){
16811         var ct = this.store.getCount();
16812         if(ct > 0){
16813             if(this.selectedIndex == -1){
16814                 this.select(0);
16815             }else if(this.selectedIndex != 0){
16816                 this.select(this.selectedIndex-1);
16817             }
16818         }
16819     },
16820
16821     // private
16822     onKeyUp : function(e){
16823         if(this.editable !== false && !e.isSpecialKey()){
16824             this.lastKey = e.getKey();
16825             this.dqTask.delay(this.queryDelay);
16826         }
16827     },
16828
16829     // private
16830     validateBlur : function(){
16831         return !this.list || !this.list.isVisible();   
16832     },
16833
16834     // private
16835     initQuery : function(){
16836         
16837         var v = this.getRawValue();
16838         
16839         if(this.tickable && this.editable){
16840             v = this.tickableInputEl().getValue();
16841         }
16842         
16843         this.doQuery(v);
16844     },
16845
16846     // private
16847     doForce : function(){
16848         if(this.inputEl().dom.value.length > 0){
16849             this.inputEl().dom.value =
16850                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16851              
16852         }
16853     },
16854
16855     /**
16856      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16857      * query allowing the query action to be canceled if needed.
16858      * @param {String} query The SQL query to execute
16859      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16860      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16861      * saved in the current store (defaults to false)
16862      */
16863     doQuery : function(q, forceAll){
16864         
16865         if(q === undefined || q === null){
16866             q = '';
16867         }
16868         var qe = {
16869             query: q,
16870             forceAll: forceAll,
16871             combo: this,
16872             cancel:false
16873         };
16874         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16875             return false;
16876         }
16877         q = qe.query;
16878         
16879         forceAll = qe.forceAll;
16880         if(forceAll === true || (q.length >= this.minChars)){
16881             
16882             this.hasQuery = true;
16883             
16884             if(this.lastQuery != q || this.alwaysQuery){
16885                 this.lastQuery = q;
16886                 if(this.mode == 'local'){
16887                     this.selectedIndex = -1;
16888                     if(forceAll){
16889                         this.store.clearFilter();
16890                     }else{
16891                         
16892                         if(this.specialFilter){
16893                             this.fireEvent('specialfilter', this);
16894                             this.onLoad();
16895                             return;
16896                         }
16897                         
16898                         this.store.filter(this.displayField, q);
16899                     }
16900                     
16901                     this.store.fireEvent("datachanged", this.store);
16902                     
16903                     this.onLoad();
16904                     
16905                     
16906                 }else{
16907                     
16908                     this.store.baseParams[this.queryParam] = q;
16909                     
16910                     var options = {params : this.getParams(q)};
16911                     
16912                     if(this.loadNext){
16913                         options.add = true;
16914                         options.params.start = this.page * this.pageSize;
16915                     }
16916                     
16917                     this.store.load(options);
16918                     
16919                     /*
16920                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16921                      *  we should expand the list on onLoad
16922                      *  so command out it
16923                      */
16924 //                    this.expand();
16925                 }
16926             }else{
16927                 this.selectedIndex = -1;
16928                 this.onLoad();   
16929             }
16930         }
16931         
16932         this.loadNext = false;
16933     },
16934     
16935     // private
16936     getParams : function(q){
16937         var p = {};
16938         //p[this.queryParam] = q;
16939         
16940         if(this.pageSize){
16941             p.start = 0;
16942             p.limit = this.pageSize;
16943         }
16944         return p;
16945     },
16946
16947     /**
16948      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16949      */
16950     collapse : function(){
16951         if(!this.isExpanded()){
16952             return;
16953         }
16954         
16955         this.list.hide();
16956         
16957         this.hasFocus = false;
16958         
16959         if(this.tickable){
16960             this.okBtn.hide();
16961             this.cancelBtn.hide();
16962             this.trigger.show();
16963             
16964             if(this.editable){
16965                 this.tickableInputEl().dom.value = '';
16966                 this.tickableInputEl().blur();
16967             }
16968             
16969         }
16970         
16971         Roo.get(document).un('mousedown', this.collapseIf, this);
16972         Roo.get(document).un('mousewheel', this.collapseIf, this);
16973         if (!this.editable) {
16974             Roo.get(document).un('keydown', this.listKeyPress, this);
16975         }
16976         this.fireEvent('collapse', this);
16977         
16978         this.validate();
16979     },
16980
16981     // private
16982     collapseIf : function(e){
16983         var in_combo  = e.within(this.el);
16984         var in_list =  e.within(this.list);
16985         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16986         
16987         if (in_combo || in_list || is_list) {
16988             //e.stopPropagation();
16989             return;
16990         }
16991         
16992         if(this.tickable){
16993             this.onTickableFooterButtonClick(e, false, false);
16994         }
16995
16996         this.collapse();
16997         
16998     },
16999
17000     /**
17001      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17002      */
17003     expand : function(){
17004        
17005         if(this.isExpanded() || !this.hasFocus){
17006             return;
17007         }
17008         
17009         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17010         this.list.setWidth(lw);
17011         
17012         Roo.log('expand');
17013         
17014         this.list.show();
17015         
17016         this.restrictHeight();
17017         
17018         if(this.tickable){
17019             
17020             this.tickItems = Roo.apply([], this.item);
17021             
17022             this.okBtn.show();
17023             this.cancelBtn.show();
17024             this.trigger.hide();
17025             
17026             if(this.editable){
17027                 this.tickableInputEl().focus();
17028             }
17029             
17030         }
17031         
17032         Roo.get(document).on('mousedown', this.collapseIf, this);
17033         Roo.get(document).on('mousewheel', this.collapseIf, this);
17034         if (!this.editable) {
17035             Roo.get(document).on('keydown', this.listKeyPress, this);
17036         }
17037         
17038         this.fireEvent('expand', this);
17039     },
17040
17041     // private
17042     // Implements the default empty TriggerField.onTriggerClick function
17043     onTriggerClick : function(e)
17044     {
17045         Roo.log('trigger click');
17046         
17047         if(this.disabled || !this.triggerList){
17048             return;
17049         }
17050         
17051         this.page = 0;
17052         this.loadNext = false;
17053         
17054         if(this.isExpanded()){
17055             this.collapse();
17056             if (!this.blockFocus) {
17057                 this.inputEl().focus();
17058             }
17059             
17060         }else {
17061             this.hasFocus = true;
17062             if(this.triggerAction == 'all') {
17063                 this.doQuery(this.allQuery, true);
17064             } else {
17065                 this.doQuery(this.getRawValue());
17066             }
17067             if (!this.blockFocus) {
17068                 this.inputEl().focus();
17069             }
17070         }
17071     },
17072     
17073     onTickableTriggerClick : function(e)
17074     {
17075         if(this.disabled){
17076             return;
17077         }
17078         
17079         this.page = 0;
17080         this.loadNext = false;
17081         this.hasFocus = true;
17082         
17083         if(this.triggerAction == 'all') {
17084             this.doQuery(this.allQuery, true);
17085         } else {
17086             this.doQuery(this.getRawValue());
17087         }
17088     },
17089     
17090     onSearchFieldClick : function(e)
17091     {
17092         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17093             this.onTickableFooterButtonClick(e, false, false);
17094             return;
17095         }
17096         
17097         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17098             return;
17099         }
17100         
17101         this.page = 0;
17102         this.loadNext = false;
17103         this.hasFocus = true;
17104         
17105         if(this.triggerAction == 'all') {
17106             this.doQuery(this.allQuery, true);
17107         } else {
17108             this.doQuery(this.getRawValue());
17109         }
17110     },
17111     
17112     listKeyPress : function(e)
17113     {
17114         //Roo.log('listkeypress');
17115         // scroll to first matching element based on key pres..
17116         if (e.isSpecialKey()) {
17117             return false;
17118         }
17119         var k = String.fromCharCode(e.getKey()).toUpperCase();
17120         //Roo.log(k);
17121         var match  = false;
17122         var csel = this.view.getSelectedNodes();
17123         var cselitem = false;
17124         if (csel.length) {
17125             var ix = this.view.indexOf(csel[0]);
17126             cselitem  = this.store.getAt(ix);
17127             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17128                 cselitem = false;
17129             }
17130             
17131         }
17132         
17133         this.store.each(function(v) { 
17134             if (cselitem) {
17135                 // start at existing selection.
17136                 if (cselitem.id == v.id) {
17137                     cselitem = false;
17138                 }
17139                 return true;
17140             }
17141                 
17142             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17143                 match = this.store.indexOf(v);
17144                 return false;
17145             }
17146             return true;
17147         }, this);
17148         
17149         if (match === false) {
17150             return true; // no more action?
17151         }
17152         // scroll to?
17153         this.view.select(match);
17154         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17155         sn.scrollIntoView(sn.dom.parentNode, false);
17156     },
17157     
17158     onViewScroll : function(e, t){
17159         
17160         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){
17161             return;
17162         }
17163         
17164         this.hasQuery = true;
17165         
17166         this.loading = this.list.select('.loading', true).first();
17167         
17168         if(this.loading === null){
17169             this.list.createChild({
17170                 tag: 'div',
17171                 cls: 'loading roo-select2-more-results roo-select2-active',
17172                 html: 'Loading more results...'
17173             });
17174             
17175             this.loading = this.list.select('.loading', true).first();
17176             
17177             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17178             
17179             this.loading.hide();
17180         }
17181         
17182         this.loading.show();
17183         
17184         var _combo = this;
17185         
17186         this.page++;
17187         this.loadNext = true;
17188         
17189         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17190         
17191         return;
17192     },
17193     
17194     addItem : function(o)
17195     {   
17196         var dv = ''; // display value
17197         
17198         if (this.displayField) {
17199             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17200         } else {
17201             // this is an error condition!!!
17202             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17203         }
17204         
17205         if(!dv.length){
17206             return;
17207         }
17208         
17209         var choice = this.choices.createChild({
17210             tag: 'li',
17211             cls: 'roo-select2-search-choice',
17212             cn: [
17213                 {
17214                     tag: 'div',
17215                     html: dv
17216                 },
17217                 {
17218                     tag: 'a',
17219                     href: '#',
17220                     cls: 'roo-select2-search-choice-close fa fa-times',
17221                     tabindex: '-1'
17222                 }
17223             ]
17224             
17225         }, this.searchField);
17226         
17227         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17228         
17229         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17230         
17231         this.item.push(o);
17232         
17233         this.lastData = o;
17234         
17235         this.syncValue();
17236         
17237         this.inputEl().dom.value = '';
17238         
17239         this.validate();
17240     },
17241     
17242     onRemoveItem : function(e, _self, o)
17243     {
17244         e.preventDefault();
17245         
17246         this.lastItem = Roo.apply([], this.item);
17247         
17248         var index = this.item.indexOf(o.data) * 1;
17249         
17250         if( index < 0){
17251             Roo.log('not this item?!');
17252             return;
17253         }
17254         
17255         this.item.splice(index, 1);
17256         o.item.remove();
17257         
17258         this.syncValue();
17259         
17260         this.fireEvent('remove', this, e);
17261         
17262         this.validate();
17263         
17264     },
17265     
17266     syncValue : function()
17267     {
17268         if(!this.item.length){
17269             this.clearValue();
17270             return;
17271         }
17272             
17273         var value = [];
17274         var _this = this;
17275         Roo.each(this.item, function(i){
17276             if(_this.valueField){
17277                 value.push(i[_this.valueField]);
17278                 return;
17279             }
17280
17281             value.push(i);
17282         });
17283
17284         this.value = value.join(',');
17285
17286         if(this.hiddenField){
17287             this.hiddenField.dom.value = this.value;
17288         }
17289         
17290         this.store.fireEvent("datachanged", this.store);
17291         
17292         this.validate();
17293     },
17294     
17295     clearItem : function()
17296     {
17297         if(!this.multiple){
17298             return;
17299         }
17300         
17301         this.item = [];
17302         
17303         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17304            c.remove();
17305         });
17306         
17307         this.syncValue();
17308         
17309         this.validate();
17310         
17311         if(this.tickable && !Roo.isTouch){
17312             this.view.refresh();
17313         }
17314     },
17315     
17316     inputEl: function ()
17317     {
17318         if(Roo.isIOS && this.useNativeIOS){
17319             return this.el.select('select.roo-ios-select', true).first();
17320         }
17321         
17322         if(Roo.isTouch && this.mobileTouchView){
17323             return this.el.select('input.form-control',true).first();
17324         }
17325         
17326         if(this.tickable){
17327             return this.searchField;
17328         }
17329         
17330         return this.el.select('input.form-control',true).first();
17331     },
17332     
17333     onTickableFooterButtonClick : function(e, btn, el)
17334     {
17335         e.preventDefault();
17336         
17337         this.lastItem = Roo.apply([], this.item);
17338         
17339         if(btn && btn.name == 'cancel'){
17340             this.tickItems = Roo.apply([], this.item);
17341             this.collapse();
17342             return;
17343         }
17344         
17345         this.clearItem();
17346         
17347         var _this = this;
17348         
17349         Roo.each(this.tickItems, function(o){
17350             _this.addItem(o);
17351         });
17352         
17353         this.collapse();
17354         
17355     },
17356     
17357     validate : function()
17358     {
17359         if(this.getVisibilityEl().hasClass('hidden')){
17360             return true;
17361         }
17362         
17363         var v = this.getRawValue();
17364         
17365         if(this.multiple){
17366             v = this.getValue();
17367         }
17368         
17369         if(this.disabled || this.allowBlank || v.length){
17370             this.markValid();
17371             return true;
17372         }
17373         
17374         this.markInvalid();
17375         return false;
17376     },
17377     
17378     tickableInputEl : function()
17379     {
17380         if(!this.tickable || !this.editable){
17381             return this.inputEl();
17382         }
17383         
17384         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17385     },
17386     
17387     
17388     getAutoCreateTouchView : function()
17389     {
17390         var id = Roo.id();
17391         
17392         var cfg = {
17393             cls: 'form-group' //input-group
17394         };
17395         
17396         var input =  {
17397             tag: 'input',
17398             id : id,
17399             type : this.inputType,
17400             cls : 'form-control x-combo-noedit',
17401             autocomplete: 'new-password',
17402             placeholder : this.placeholder || '',
17403             readonly : true
17404         };
17405         
17406         if (this.name) {
17407             input.name = this.name;
17408         }
17409         
17410         if (this.size) {
17411             input.cls += ' input-' + this.size;
17412         }
17413         
17414         if (this.disabled) {
17415             input.disabled = true;
17416         }
17417         
17418         var inputblock = {
17419             cls : 'roo-combobox-wrap',
17420             cn : [
17421                 input
17422             ]
17423         };
17424         
17425         if(this.before){
17426             inputblock.cls += ' input-group';
17427             
17428             inputblock.cn.unshift({
17429                 tag :'span',
17430                 cls : 'input-group-addon input-group-prepend input-group-text',
17431                 html : this.before
17432             });
17433         }
17434         
17435         if(this.removable && !this.multiple){
17436             inputblock.cls += ' roo-removable';
17437             
17438             inputblock.cn.push({
17439                 tag: 'button',
17440                 html : 'x',
17441                 cls : 'roo-combo-removable-btn close'
17442             });
17443         }
17444
17445         if(this.hasFeedback && !this.allowBlank){
17446             
17447             inputblock.cls += ' has-feedback';
17448             
17449             inputblock.cn.push({
17450                 tag: 'span',
17451                 cls: 'glyphicon form-control-feedback'
17452             });
17453             
17454         }
17455         
17456         if (this.after) {
17457             
17458             inputblock.cls += (this.before) ? '' : ' input-group';
17459             
17460             inputblock.cn.push({
17461                 tag :'span',
17462                 cls : 'input-group-addon input-group-append input-group-text',
17463                 html : this.after
17464             });
17465         }
17466
17467         
17468         var ibwrap = inputblock;
17469         
17470         if(this.multiple){
17471             ibwrap = {
17472                 tag: 'ul',
17473                 cls: 'roo-select2-choices',
17474                 cn:[
17475                     {
17476                         tag: 'li',
17477                         cls: 'roo-select2-search-field',
17478                         cn: [
17479
17480                             inputblock
17481                         ]
17482                     }
17483                 ]
17484             };
17485         
17486             
17487         }
17488         
17489         var combobox = {
17490             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17491             cn: [
17492                 {
17493                     tag: 'input',
17494                     type : 'hidden',
17495                     cls: 'form-hidden-field'
17496                 },
17497                 ibwrap
17498             ]
17499         };
17500         
17501         if(!this.multiple && this.showToggleBtn){
17502             
17503             var caret = {
17504                 cls: 'caret'
17505             };
17506             
17507             if (this.caret != false) {
17508                 caret = {
17509                      tag: 'i',
17510                      cls: 'fa fa-' + this.caret
17511                 };
17512                 
17513             }
17514             
17515             combobox.cn.push({
17516                 tag :'span',
17517                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17518                 cn : [
17519                     Roo.bootstrap.version == 3 ? caret : '',
17520                     {
17521                         tag: 'span',
17522                         cls: 'combobox-clear',
17523                         cn  : [
17524                             {
17525                                 tag : 'i',
17526                                 cls: 'icon-remove'
17527                             }
17528                         ]
17529                     }
17530                 ]
17531
17532             })
17533         }
17534         
17535         if(this.multiple){
17536             combobox.cls += ' roo-select2-container-multi';
17537         }
17538         
17539         var align = this.labelAlign || this.parentLabelAlign();
17540         
17541         if (align ==='left' && this.fieldLabel.length) {
17542
17543             cfg.cn = [
17544                 {
17545                    tag : 'i',
17546                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17547                    tooltip : 'This field is required'
17548                 },
17549                 {
17550                     tag: 'label',
17551                     cls : 'control-label col-form-label',
17552                     html : this.fieldLabel
17553
17554                 },
17555                 {
17556                     cls : 'roo-combobox-wrap ', 
17557                     cn: [
17558                         combobox
17559                     ]
17560                 }
17561             ];
17562             
17563             var labelCfg = cfg.cn[1];
17564             var contentCfg = cfg.cn[2];
17565             
17566
17567             if(this.indicatorpos == 'right'){
17568                 cfg.cn = [
17569                     {
17570                         tag: 'label',
17571                         'for' :  id,
17572                         cls : 'control-label col-form-label',
17573                         cn : [
17574                             {
17575                                 tag : 'span',
17576                                 html : this.fieldLabel
17577                             },
17578                             {
17579                                 tag : 'i',
17580                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17581                                 tooltip : 'This field is required'
17582                             }
17583                         ]
17584                     },
17585                     {
17586                         cls : "roo-combobox-wrap ",
17587                         cn: [
17588                             combobox
17589                         ]
17590                     }
17591
17592                 ];
17593                 
17594                 labelCfg = cfg.cn[0];
17595                 contentCfg = cfg.cn[1];
17596             }
17597             
17598            
17599             
17600             if(this.labelWidth > 12){
17601                 labelCfg.style = "width: " + this.labelWidth + 'px';
17602             }
17603            
17604             if(this.labelWidth < 13 && this.labelmd == 0){
17605                 this.labelmd = this.labelWidth;
17606             }
17607             
17608             if(this.labellg > 0){
17609                 labelCfg.cls += ' col-lg-' + this.labellg;
17610                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17611             }
17612             
17613             if(this.labelmd > 0){
17614                 labelCfg.cls += ' col-md-' + this.labelmd;
17615                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17616             }
17617             
17618             if(this.labelsm > 0){
17619                 labelCfg.cls += ' col-sm-' + this.labelsm;
17620                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17621             }
17622             
17623             if(this.labelxs > 0){
17624                 labelCfg.cls += ' col-xs-' + this.labelxs;
17625                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17626             }
17627                 
17628                 
17629         } else if ( this.fieldLabel.length) {
17630             cfg.cn = [
17631                 {
17632                    tag : 'i',
17633                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17634                    tooltip : 'This field is required'
17635                 },
17636                 {
17637                     tag: 'label',
17638                     cls : 'control-label',
17639                     html : this.fieldLabel
17640
17641                 },
17642                 {
17643                     cls : '', 
17644                     cn: [
17645                         combobox
17646                     ]
17647                 }
17648             ];
17649             
17650             if(this.indicatorpos == 'right'){
17651                 cfg.cn = [
17652                     {
17653                         tag: 'label',
17654                         cls : 'control-label',
17655                         html : this.fieldLabel,
17656                         cn : [
17657                             {
17658                                tag : 'i',
17659                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17660                                tooltip : 'This field is required'
17661                             }
17662                         ]
17663                     },
17664                     {
17665                         cls : '', 
17666                         cn: [
17667                             combobox
17668                         ]
17669                     }
17670                 ];
17671             }
17672         } else {
17673             cfg.cn = combobox;    
17674         }
17675         
17676         
17677         var settings = this;
17678         
17679         ['xs','sm','md','lg'].map(function(size){
17680             if (settings[size]) {
17681                 cfg.cls += ' col-' + size + '-' + settings[size];
17682             }
17683         });
17684         
17685         return cfg;
17686     },
17687     
17688     initTouchView : function()
17689     {
17690         this.renderTouchView();
17691         
17692         this.touchViewEl.on('scroll', function(){
17693             this.el.dom.scrollTop = 0;
17694         }, this);
17695         
17696         this.originalValue = this.getValue();
17697         
17698         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17699         
17700         this.inputEl().on("click", this.showTouchView, this);
17701         if (this.triggerEl) {
17702             this.triggerEl.on("click", this.showTouchView, this);
17703         }
17704         
17705         
17706         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17707         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17708         
17709         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17710         
17711         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17712         this.store.on('load', this.onTouchViewLoad, this);
17713         this.store.on('loadexception', this.onTouchViewLoadException, this);
17714         
17715         if(this.hiddenName){
17716             
17717             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17718             
17719             this.hiddenField.dom.value =
17720                 this.hiddenValue !== undefined ? this.hiddenValue :
17721                 this.value !== undefined ? this.value : '';
17722         
17723             this.el.dom.removeAttribute('name');
17724             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17725         }
17726         
17727         if(this.multiple){
17728             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17729             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17730         }
17731         
17732         if(this.removable && !this.multiple){
17733             var close = this.closeTriggerEl();
17734             if(close){
17735                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17736                 close.on('click', this.removeBtnClick, this, close);
17737             }
17738         }
17739         /*
17740          * fix the bug in Safari iOS8
17741          */
17742         this.inputEl().on("focus", function(e){
17743             document.activeElement.blur();
17744         }, this);
17745         
17746         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17747         
17748         return;
17749         
17750         
17751     },
17752     
17753     renderTouchView : function()
17754     {
17755         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17756         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17757         
17758         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17759         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17760         
17761         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17762         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17763         this.touchViewBodyEl.setStyle('overflow', 'auto');
17764         
17765         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17766         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17767         
17768         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17769         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17770         
17771     },
17772     
17773     showTouchView : function()
17774     {
17775         if(this.disabled){
17776             return;
17777         }
17778         
17779         this.touchViewHeaderEl.hide();
17780
17781         if(this.modalTitle.length){
17782             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17783             this.touchViewHeaderEl.show();
17784         }
17785
17786         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17787         this.touchViewEl.show();
17788
17789         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17790         
17791         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17792         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17793
17794         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17795
17796         if(this.modalTitle.length){
17797             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17798         }
17799         
17800         this.touchViewBodyEl.setHeight(bodyHeight);
17801
17802         if(this.animate){
17803             var _this = this;
17804             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17805         }else{
17806             this.touchViewEl.addClass(['in','show']);
17807         }
17808         
17809         if(this._touchViewMask){
17810             Roo.get(document.body).addClass("x-body-masked");
17811             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17812             this._touchViewMask.setStyle('z-index', 10000);
17813             this._touchViewMask.addClass('show');
17814         }
17815         
17816         this.doTouchViewQuery();
17817         
17818     },
17819     
17820     hideTouchView : function()
17821     {
17822         this.touchViewEl.removeClass(['in','show']);
17823
17824         if(this.animate){
17825             var _this = this;
17826             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17827         }else{
17828             this.touchViewEl.setStyle('display', 'none');
17829         }
17830         
17831         if(this._touchViewMask){
17832             this._touchViewMask.removeClass('show');
17833             Roo.get(document.body).removeClass("x-body-masked");
17834         }
17835     },
17836     
17837     setTouchViewValue : function()
17838     {
17839         if(this.multiple){
17840             this.clearItem();
17841         
17842             var _this = this;
17843
17844             Roo.each(this.tickItems, function(o){
17845                 this.addItem(o);
17846             }, this);
17847         }
17848         
17849         this.hideTouchView();
17850     },
17851     
17852     doTouchViewQuery : function()
17853     {
17854         var qe = {
17855             query: '',
17856             forceAll: true,
17857             combo: this,
17858             cancel:false
17859         };
17860         
17861         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17862             return false;
17863         }
17864         
17865         if(!this.alwaysQuery || this.mode == 'local'){
17866             this.onTouchViewLoad();
17867             return;
17868         }
17869         
17870         this.store.load();
17871     },
17872     
17873     onTouchViewBeforeLoad : function(combo,opts)
17874     {
17875         return;
17876     },
17877
17878     // private
17879     onTouchViewLoad : function()
17880     {
17881         if(this.store.getCount() < 1){
17882             this.onTouchViewEmptyResults();
17883             return;
17884         }
17885         
17886         this.clearTouchView();
17887         
17888         var rawValue = this.getRawValue();
17889         
17890         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17891         
17892         this.tickItems = [];
17893         
17894         this.store.data.each(function(d, rowIndex){
17895             var row = this.touchViewListGroup.createChild(template);
17896             
17897             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17898                 row.addClass(d.data.cls);
17899             }
17900             
17901             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17902                 var cfg = {
17903                     data : d.data,
17904                     html : d.data[this.displayField]
17905                 };
17906                 
17907                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17908                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17909                 }
17910             }
17911             row.removeClass('selected');
17912             if(!this.multiple && this.valueField &&
17913                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17914             {
17915                 // radio buttons..
17916                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17917                 row.addClass('selected');
17918             }
17919             
17920             if(this.multiple && this.valueField &&
17921                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17922             {
17923                 
17924                 // checkboxes...
17925                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17926                 this.tickItems.push(d.data);
17927             }
17928             
17929             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17930             
17931         }, this);
17932         
17933         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17934         
17935         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17936
17937         if(this.modalTitle.length){
17938             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17939         }
17940
17941         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17942         
17943         if(this.mobile_restrict_height && listHeight < bodyHeight){
17944             this.touchViewBodyEl.setHeight(listHeight);
17945         }
17946         
17947         var _this = this;
17948         
17949         if(firstChecked && listHeight > bodyHeight){
17950             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17951         }
17952         
17953     },
17954     
17955     onTouchViewLoadException : function()
17956     {
17957         this.hideTouchView();
17958     },
17959     
17960     onTouchViewEmptyResults : function()
17961     {
17962         this.clearTouchView();
17963         
17964         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17965         
17966         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17967         
17968     },
17969     
17970     clearTouchView : function()
17971     {
17972         this.touchViewListGroup.dom.innerHTML = '';
17973     },
17974     
17975     onTouchViewClick : function(e, el, o)
17976     {
17977         e.preventDefault();
17978         
17979         var row = o.row;
17980         var rowIndex = o.rowIndex;
17981         
17982         var r = this.store.getAt(rowIndex);
17983         
17984         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17985             
17986             if(!this.multiple){
17987                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17988                     c.dom.removeAttribute('checked');
17989                 }, this);
17990
17991                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17992
17993                 this.setFromData(r.data);
17994
17995                 var close = this.closeTriggerEl();
17996
17997                 if(close){
17998                     close.show();
17999                 }
18000
18001                 this.hideTouchView();
18002
18003                 this.fireEvent('select', this, r, rowIndex);
18004
18005                 return;
18006             }
18007
18008             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18009                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18010                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18011                 return;
18012             }
18013
18014             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18015             this.addItem(r.data);
18016             this.tickItems.push(r.data);
18017         }
18018     },
18019     
18020     getAutoCreateNativeIOS : function()
18021     {
18022         var cfg = {
18023             cls: 'form-group' //input-group,
18024         };
18025         
18026         var combobox =  {
18027             tag: 'select',
18028             cls : 'roo-ios-select'
18029         };
18030         
18031         if (this.name) {
18032             combobox.name = this.name;
18033         }
18034         
18035         if (this.disabled) {
18036             combobox.disabled = true;
18037         }
18038         
18039         var settings = this;
18040         
18041         ['xs','sm','md','lg'].map(function(size){
18042             if (settings[size]) {
18043                 cfg.cls += ' col-' + size + '-' + settings[size];
18044             }
18045         });
18046         
18047         cfg.cn = combobox;
18048         
18049         return cfg;
18050         
18051     },
18052     
18053     initIOSView : function()
18054     {
18055         this.store.on('load', this.onIOSViewLoad, this);
18056         
18057         return;
18058     },
18059     
18060     onIOSViewLoad : function()
18061     {
18062         if(this.store.getCount() < 1){
18063             return;
18064         }
18065         
18066         this.clearIOSView();
18067         
18068         if(this.allowBlank) {
18069             
18070             var default_text = '-- SELECT --';
18071             
18072             if(this.placeholder.length){
18073                 default_text = this.placeholder;
18074             }
18075             
18076             if(this.emptyTitle.length){
18077                 default_text += ' - ' + this.emptyTitle + ' -';
18078             }
18079             
18080             var opt = this.inputEl().createChild({
18081                 tag: 'option',
18082                 value : 0,
18083                 html : default_text
18084             });
18085             
18086             var o = {};
18087             o[this.valueField] = 0;
18088             o[this.displayField] = default_text;
18089             
18090             this.ios_options.push({
18091                 data : o,
18092                 el : opt
18093             });
18094             
18095         }
18096         
18097         this.store.data.each(function(d, rowIndex){
18098             
18099             var html = '';
18100             
18101             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18102                 html = d.data[this.displayField];
18103             }
18104             
18105             var value = '';
18106             
18107             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18108                 value = d.data[this.valueField];
18109             }
18110             
18111             var option = {
18112                 tag: 'option',
18113                 value : value,
18114                 html : html
18115             };
18116             
18117             if(this.value == d.data[this.valueField]){
18118                 option['selected'] = true;
18119             }
18120             
18121             var opt = this.inputEl().createChild(option);
18122             
18123             this.ios_options.push({
18124                 data : d.data,
18125                 el : opt
18126             });
18127             
18128         }, this);
18129         
18130         this.inputEl().on('change', function(){
18131            this.fireEvent('select', this);
18132         }, this);
18133         
18134     },
18135     
18136     clearIOSView: function()
18137     {
18138         this.inputEl().dom.innerHTML = '';
18139         
18140         this.ios_options = [];
18141     },
18142     
18143     setIOSValue: function(v)
18144     {
18145         this.value = v;
18146         
18147         if(!this.ios_options){
18148             return;
18149         }
18150         
18151         Roo.each(this.ios_options, function(opts){
18152            
18153            opts.el.dom.removeAttribute('selected');
18154            
18155            if(opts.data[this.valueField] != v){
18156                return;
18157            }
18158            
18159            opts.el.dom.setAttribute('selected', true);
18160            
18161         }, this);
18162     }
18163
18164     /** 
18165     * @cfg {Boolean} grow 
18166     * @hide 
18167     */
18168     /** 
18169     * @cfg {Number} growMin 
18170     * @hide 
18171     */
18172     /** 
18173     * @cfg {Number} growMax 
18174     * @hide 
18175     */
18176     /**
18177      * @hide
18178      * @method autoSize
18179      */
18180 });
18181
18182 Roo.apply(Roo.bootstrap.ComboBox,  {
18183     
18184     header : {
18185         tag: 'div',
18186         cls: 'modal-header',
18187         cn: [
18188             {
18189                 tag: 'h4',
18190                 cls: 'modal-title'
18191             }
18192         ]
18193     },
18194     
18195     body : {
18196         tag: 'div',
18197         cls: 'modal-body',
18198         cn: [
18199             {
18200                 tag: 'ul',
18201                 cls: 'list-group'
18202             }
18203         ]
18204     },
18205     
18206     listItemRadio : {
18207         tag: 'li',
18208         cls: 'list-group-item',
18209         cn: [
18210             {
18211                 tag: 'span',
18212                 cls: 'roo-combobox-list-group-item-value'
18213             },
18214             {
18215                 tag: 'div',
18216                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18217                 cn: [
18218                     {
18219                         tag: 'input',
18220                         type: 'radio'
18221                     },
18222                     {
18223                         tag: 'label'
18224                     }
18225                 ]
18226             }
18227         ]
18228     },
18229     
18230     listItemCheckbox : {
18231         tag: 'li',
18232         cls: 'list-group-item',
18233         cn: [
18234             {
18235                 tag: 'span',
18236                 cls: 'roo-combobox-list-group-item-value'
18237             },
18238             {
18239                 tag: 'div',
18240                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18241                 cn: [
18242                     {
18243                         tag: 'input',
18244                         type: 'checkbox'
18245                     },
18246                     {
18247                         tag: 'label'
18248                     }
18249                 ]
18250             }
18251         ]
18252     },
18253     
18254     emptyResult : {
18255         tag: 'div',
18256         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18257     },
18258     
18259     footer : {
18260         tag: 'div',
18261         cls: 'modal-footer',
18262         cn: [
18263             {
18264                 tag: 'div',
18265                 cls: 'row',
18266                 cn: [
18267                     {
18268                         tag: 'div',
18269                         cls: 'col-xs-6 text-left',
18270                         cn: {
18271                             tag: 'button',
18272                             cls: 'btn btn-danger roo-touch-view-cancel',
18273                             html: 'Cancel'
18274                         }
18275                     },
18276                     {
18277                         tag: 'div',
18278                         cls: 'col-xs-6 text-right',
18279                         cn: {
18280                             tag: 'button',
18281                             cls: 'btn btn-success roo-touch-view-ok',
18282                             html: 'OK'
18283                         }
18284                     }
18285                 ]
18286             }
18287         ]
18288         
18289     }
18290 });
18291
18292 Roo.apply(Roo.bootstrap.ComboBox,  {
18293     
18294     touchViewTemplate : {
18295         tag: 'div',
18296         cls: 'modal fade roo-combobox-touch-view',
18297         cn: [
18298             {
18299                 tag: 'div',
18300                 cls: 'modal-dialog',
18301                 style : 'position:fixed', // we have to fix position....
18302                 cn: [
18303                     {
18304                         tag: 'div',
18305                         cls: 'modal-content',
18306                         cn: [
18307                             Roo.bootstrap.ComboBox.header,
18308                             Roo.bootstrap.ComboBox.body,
18309                             Roo.bootstrap.ComboBox.footer
18310                         ]
18311                     }
18312                 ]
18313             }
18314         ]
18315     }
18316 });/*
18317  * Based on:
18318  * Ext JS Library 1.1.1
18319  * Copyright(c) 2006-2007, Ext JS, LLC.
18320  *
18321  * Originally Released Under LGPL - original licence link has changed is not relivant.
18322  *
18323  * Fork - LGPL
18324  * <script type="text/javascript">
18325  */
18326
18327 /**
18328  * @class Roo.View
18329  * @extends Roo.util.Observable
18330  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18331  * This class also supports single and multi selection modes. <br>
18332  * Create a data model bound view:
18333  <pre><code>
18334  var store = new Roo.data.Store(...);
18335
18336  var view = new Roo.View({
18337     el : "my-element",
18338     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18339  
18340     singleSelect: true,
18341     selectedClass: "ydataview-selected",
18342     store: store
18343  });
18344
18345  // listen for node click?
18346  view.on("click", function(vw, index, node, e){
18347  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18348  });
18349
18350  // load XML data
18351  dataModel.load("foobar.xml");
18352  </code></pre>
18353  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18354  * <br><br>
18355  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18356  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18357  * 
18358  * Note: old style constructor is still suported (container, template, config)
18359  * 
18360  * @constructor
18361  * Create a new View
18362  * @param {Object} config The config object
18363  * 
18364  */
18365 Roo.View = function(config, depreciated_tpl, depreciated_config){
18366     
18367     this.parent = false;
18368     
18369     if (typeof(depreciated_tpl) == 'undefined') {
18370         // new way.. - universal constructor.
18371         Roo.apply(this, config);
18372         this.el  = Roo.get(this.el);
18373     } else {
18374         // old format..
18375         this.el  = Roo.get(config);
18376         this.tpl = depreciated_tpl;
18377         Roo.apply(this, depreciated_config);
18378     }
18379     this.wrapEl  = this.el.wrap().wrap();
18380     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18381     
18382     
18383     if(typeof(this.tpl) == "string"){
18384         this.tpl = new Roo.Template(this.tpl);
18385     } else {
18386         // support xtype ctors..
18387         this.tpl = new Roo.factory(this.tpl, Roo);
18388     }
18389     
18390     
18391     this.tpl.compile();
18392     
18393     /** @private */
18394     this.addEvents({
18395         /**
18396          * @event beforeclick
18397          * Fires before a click is processed. Returns false to cancel the default action.
18398          * @param {Roo.View} this
18399          * @param {Number} index The index of the target node
18400          * @param {HTMLElement} node The target node
18401          * @param {Roo.EventObject} e The raw event object
18402          */
18403             "beforeclick" : true,
18404         /**
18405          * @event click
18406          * Fires when a template node is clicked.
18407          * @param {Roo.View} this
18408          * @param {Number} index The index of the target node
18409          * @param {HTMLElement} node The target node
18410          * @param {Roo.EventObject} e The raw event object
18411          */
18412             "click" : true,
18413         /**
18414          * @event dblclick
18415          * Fires when a template node is double clicked.
18416          * @param {Roo.View} this
18417          * @param {Number} index The index of the target node
18418          * @param {HTMLElement} node The target node
18419          * @param {Roo.EventObject} e The raw event object
18420          */
18421             "dblclick" : true,
18422         /**
18423          * @event contextmenu
18424          * Fires when a template node is right clicked.
18425          * @param {Roo.View} this
18426          * @param {Number} index The index of the target node
18427          * @param {HTMLElement} node The target node
18428          * @param {Roo.EventObject} e The raw event object
18429          */
18430             "contextmenu" : true,
18431         /**
18432          * @event selectionchange
18433          * Fires when the selected nodes change.
18434          * @param {Roo.View} this
18435          * @param {Array} selections Array of the selected nodes
18436          */
18437             "selectionchange" : true,
18438     
18439         /**
18440          * @event beforeselect
18441          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18442          * @param {Roo.View} this
18443          * @param {HTMLElement} node The node to be selected
18444          * @param {Array} selections Array of currently selected nodes
18445          */
18446             "beforeselect" : true,
18447         /**
18448          * @event preparedata
18449          * Fires on every row to render, to allow you to change the data.
18450          * @param {Roo.View} this
18451          * @param {Object} data to be rendered (change this)
18452          */
18453           "preparedata" : true
18454           
18455           
18456         });
18457
18458
18459
18460     this.el.on({
18461         "click": this.onClick,
18462         "dblclick": this.onDblClick,
18463         "contextmenu": this.onContextMenu,
18464         scope:this
18465     });
18466
18467     this.selections = [];
18468     this.nodes = [];
18469     this.cmp = new Roo.CompositeElementLite([]);
18470     if(this.store){
18471         this.store = Roo.factory(this.store, Roo.data);
18472         this.setStore(this.store, true);
18473     }
18474     
18475     if ( this.footer && this.footer.xtype) {
18476            
18477          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18478         
18479         this.footer.dataSource = this.store;
18480         this.footer.container = fctr;
18481         this.footer = Roo.factory(this.footer, Roo);
18482         fctr.insertFirst(this.el);
18483         
18484         // this is a bit insane - as the paging toolbar seems to detach the el..
18485 //        dom.parentNode.parentNode.parentNode
18486          // they get detached?
18487     }
18488     
18489     
18490     Roo.View.superclass.constructor.call(this);
18491     
18492     
18493 };
18494
18495 Roo.extend(Roo.View, Roo.util.Observable, {
18496     
18497      /**
18498      * @cfg {Roo.data.Store} store Data store to load data from.
18499      */
18500     store : false,
18501     
18502     /**
18503      * @cfg {String|Roo.Element} el The container element.
18504      */
18505     el : '',
18506     
18507     /**
18508      * @cfg {String|Roo.Template} tpl The template used by this View 
18509      */
18510     tpl : false,
18511     /**
18512      * @cfg {String} dataName the named area of the template to use as the data area
18513      *                          Works with domtemplates roo-name="name"
18514      */
18515     dataName: false,
18516     /**
18517      * @cfg {String} selectedClass The css class to add to selected nodes
18518      */
18519     selectedClass : "x-view-selected",
18520      /**
18521      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18522      */
18523     emptyText : "",
18524     
18525     /**
18526      * @cfg {String} text to display on mask (default Loading)
18527      */
18528     mask : false,
18529     /**
18530      * @cfg {Boolean} multiSelect Allow multiple selection
18531      */
18532     multiSelect : false,
18533     /**
18534      * @cfg {Boolean} singleSelect Allow single selection
18535      */
18536     singleSelect:  false,
18537     
18538     /**
18539      * @cfg {Boolean} toggleSelect - selecting 
18540      */
18541     toggleSelect : false,
18542     
18543     /**
18544      * @cfg {Boolean} tickable - selecting 
18545      */
18546     tickable : false,
18547     
18548     /**
18549      * Returns the element this view is bound to.
18550      * @return {Roo.Element}
18551      */
18552     getEl : function(){
18553         return this.wrapEl;
18554     },
18555     
18556     
18557
18558     /**
18559      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18560      */
18561     refresh : function(){
18562         //Roo.log('refresh');
18563         var t = this.tpl;
18564         
18565         // if we are using something like 'domtemplate', then
18566         // the what gets used is:
18567         // t.applySubtemplate(NAME, data, wrapping data..)
18568         // the outer template then get' applied with
18569         //     the store 'extra data'
18570         // and the body get's added to the
18571         //      roo-name="data" node?
18572         //      <span class='roo-tpl-{name}'></span> ?????
18573         
18574         
18575         
18576         this.clearSelections();
18577         this.el.update("");
18578         var html = [];
18579         var records = this.store.getRange();
18580         if(records.length < 1) {
18581             
18582             // is this valid??  = should it render a template??
18583             
18584             this.el.update(this.emptyText);
18585             return;
18586         }
18587         var el = this.el;
18588         if (this.dataName) {
18589             this.el.update(t.apply(this.store.meta)); //????
18590             el = this.el.child('.roo-tpl-' + this.dataName);
18591         }
18592         
18593         for(var i = 0, len = records.length; i < len; i++){
18594             var data = this.prepareData(records[i].data, i, records[i]);
18595             this.fireEvent("preparedata", this, data, i, records[i]);
18596             
18597             var d = Roo.apply({}, data);
18598             
18599             if(this.tickable){
18600                 Roo.apply(d, {'roo-id' : Roo.id()});
18601                 
18602                 var _this = this;
18603             
18604                 Roo.each(this.parent.item, function(item){
18605                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18606                         return;
18607                     }
18608                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18609                 });
18610             }
18611             
18612             html[html.length] = Roo.util.Format.trim(
18613                 this.dataName ?
18614                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18615                     t.apply(d)
18616             );
18617         }
18618         
18619         
18620         
18621         el.update(html.join(""));
18622         this.nodes = el.dom.childNodes;
18623         this.updateIndexes(0);
18624     },
18625     
18626
18627     /**
18628      * Function to override to reformat the data that is sent to
18629      * the template for each node.
18630      * DEPRICATED - use the preparedata event handler.
18631      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18632      * a JSON object for an UpdateManager bound view).
18633      */
18634     prepareData : function(data, index, record)
18635     {
18636         this.fireEvent("preparedata", this, data, index, record);
18637         return data;
18638     },
18639
18640     onUpdate : function(ds, record){
18641         // Roo.log('on update');   
18642         this.clearSelections();
18643         var index = this.store.indexOf(record);
18644         var n = this.nodes[index];
18645         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18646         n.parentNode.removeChild(n);
18647         this.updateIndexes(index, index);
18648     },
18649
18650     
18651     
18652 // --------- FIXME     
18653     onAdd : function(ds, records, index)
18654     {
18655         //Roo.log(['on Add', ds, records, index] );        
18656         this.clearSelections();
18657         if(this.nodes.length == 0){
18658             this.refresh();
18659             return;
18660         }
18661         var n = this.nodes[index];
18662         for(var i = 0, len = records.length; i < len; i++){
18663             var d = this.prepareData(records[i].data, i, records[i]);
18664             if(n){
18665                 this.tpl.insertBefore(n, d);
18666             }else{
18667                 
18668                 this.tpl.append(this.el, d);
18669             }
18670         }
18671         this.updateIndexes(index);
18672     },
18673
18674     onRemove : function(ds, record, index){
18675        // Roo.log('onRemove');
18676         this.clearSelections();
18677         var el = this.dataName  ?
18678             this.el.child('.roo-tpl-' + this.dataName) :
18679             this.el; 
18680         
18681         el.dom.removeChild(this.nodes[index]);
18682         this.updateIndexes(index);
18683     },
18684
18685     /**
18686      * Refresh an individual node.
18687      * @param {Number} index
18688      */
18689     refreshNode : function(index){
18690         this.onUpdate(this.store, this.store.getAt(index));
18691     },
18692
18693     updateIndexes : function(startIndex, endIndex){
18694         var ns = this.nodes;
18695         startIndex = startIndex || 0;
18696         endIndex = endIndex || ns.length - 1;
18697         for(var i = startIndex; i <= endIndex; i++){
18698             ns[i].nodeIndex = i;
18699         }
18700     },
18701
18702     /**
18703      * Changes the data store this view uses and refresh the view.
18704      * @param {Store} store
18705      */
18706     setStore : function(store, initial){
18707         if(!initial && this.store){
18708             this.store.un("datachanged", this.refresh);
18709             this.store.un("add", this.onAdd);
18710             this.store.un("remove", this.onRemove);
18711             this.store.un("update", this.onUpdate);
18712             this.store.un("clear", this.refresh);
18713             this.store.un("beforeload", this.onBeforeLoad);
18714             this.store.un("load", this.onLoad);
18715             this.store.un("loadexception", this.onLoad);
18716         }
18717         if(store){
18718           
18719             store.on("datachanged", this.refresh, this);
18720             store.on("add", this.onAdd, this);
18721             store.on("remove", this.onRemove, this);
18722             store.on("update", this.onUpdate, this);
18723             store.on("clear", this.refresh, this);
18724             store.on("beforeload", this.onBeforeLoad, this);
18725             store.on("load", this.onLoad, this);
18726             store.on("loadexception", this.onLoad, this);
18727         }
18728         
18729         if(store){
18730             this.refresh();
18731         }
18732     },
18733     /**
18734      * onbeforeLoad - masks the loading area.
18735      *
18736      */
18737     onBeforeLoad : function(store,opts)
18738     {
18739          //Roo.log('onBeforeLoad');   
18740         if (!opts.add) {
18741             this.el.update("");
18742         }
18743         this.el.mask(this.mask ? this.mask : "Loading" ); 
18744     },
18745     onLoad : function ()
18746     {
18747         this.el.unmask();
18748     },
18749     
18750
18751     /**
18752      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18753      * @param {HTMLElement} node
18754      * @return {HTMLElement} The template node
18755      */
18756     findItemFromChild : function(node){
18757         var el = this.dataName  ?
18758             this.el.child('.roo-tpl-' + this.dataName,true) :
18759             this.el.dom; 
18760         
18761         if(!node || node.parentNode == el){
18762                     return node;
18763             }
18764             var p = node.parentNode;
18765             while(p && p != el){
18766             if(p.parentNode == el){
18767                 return p;
18768             }
18769             p = p.parentNode;
18770         }
18771             return null;
18772     },
18773
18774     /** @ignore */
18775     onClick : function(e){
18776         var item = this.findItemFromChild(e.getTarget());
18777         if(item){
18778             var index = this.indexOf(item);
18779             if(this.onItemClick(item, index, e) !== false){
18780                 this.fireEvent("click", this, index, item, e);
18781             }
18782         }else{
18783             this.clearSelections();
18784         }
18785     },
18786
18787     /** @ignore */
18788     onContextMenu : function(e){
18789         var item = this.findItemFromChild(e.getTarget());
18790         if(item){
18791             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18792         }
18793     },
18794
18795     /** @ignore */
18796     onDblClick : function(e){
18797         var item = this.findItemFromChild(e.getTarget());
18798         if(item){
18799             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18800         }
18801     },
18802
18803     onItemClick : function(item, index, e)
18804     {
18805         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18806             return false;
18807         }
18808         if (this.toggleSelect) {
18809             var m = this.isSelected(item) ? 'unselect' : 'select';
18810             //Roo.log(m);
18811             var _t = this;
18812             _t[m](item, true, false);
18813             return true;
18814         }
18815         if(this.multiSelect || this.singleSelect){
18816             if(this.multiSelect && e.shiftKey && this.lastSelection){
18817                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18818             }else{
18819                 this.select(item, this.multiSelect && e.ctrlKey);
18820                 this.lastSelection = item;
18821             }
18822             
18823             if(!this.tickable){
18824                 e.preventDefault();
18825             }
18826             
18827         }
18828         return true;
18829     },
18830
18831     /**
18832      * Get the number of selected nodes.
18833      * @return {Number}
18834      */
18835     getSelectionCount : function(){
18836         return this.selections.length;
18837     },
18838
18839     /**
18840      * Get the currently selected nodes.
18841      * @return {Array} An array of HTMLElements
18842      */
18843     getSelectedNodes : function(){
18844         return this.selections;
18845     },
18846
18847     /**
18848      * Get the indexes of the selected nodes.
18849      * @return {Array}
18850      */
18851     getSelectedIndexes : function(){
18852         var indexes = [], s = this.selections;
18853         for(var i = 0, len = s.length; i < len; i++){
18854             indexes.push(s[i].nodeIndex);
18855         }
18856         return indexes;
18857     },
18858
18859     /**
18860      * Clear all selections
18861      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18862      */
18863     clearSelections : function(suppressEvent){
18864         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18865             this.cmp.elements = this.selections;
18866             this.cmp.removeClass(this.selectedClass);
18867             this.selections = [];
18868             if(!suppressEvent){
18869                 this.fireEvent("selectionchange", this, this.selections);
18870             }
18871         }
18872     },
18873
18874     /**
18875      * Returns true if the passed node is selected
18876      * @param {HTMLElement/Number} node The node or node index
18877      * @return {Boolean}
18878      */
18879     isSelected : function(node){
18880         var s = this.selections;
18881         if(s.length < 1){
18882             return false;
18883         }
18884         node = this.getNode(node);
18885         return s.indexOf(node) !== -1;
18886     },
18887
18888     /**
18889      * Selects nodes.
18890      * @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
18891      * @param {Boolean} keepExisting (optional) true to keep existing selections
18892      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18893      */
18894     select : function(nodeInfo, keepExisting, suppressEvent){
18895         if(nodeInfo instanceof Array){
18896             if(!keepExisting){
18897                 this.clearSelections(true);
18898             }
18899             for(var i = 0, len = nodeInfo.length; i < len; i++){
18900                 this.select(nodeInfo[i], true, true);
18901             }
18902             return;
18903         } 
18904         var node = this.getNode(nodeInfo);
18905         if(!node || this.isSelected(node)){
18906             return; // already selected.
18907         }
18908         if(!keepExisting){
18909             this.clearSelections(true);
18910         }
18911         
18912         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18913             Roo.fly(node).addClass(this.selectedClass);
18914             this.selections.push(node);
18915             if(!suppressEvent){
18916                 this.fireEvent("selectionchange", this, this.selections);
18917             }
18918         }
18919         
18920         
18921     },
18922       /**
18923      * Unselects nodes.
18924      * @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
18925      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18926      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18927      */
18928     unselect : function(nodeInfo, keepExisting, suppressEvent)
18929     {
18930         if(nodeInfo instanceof Array){
18931             Roo.each(this.selections, function(s) {
18932                 this.unselect(s, nodeInfo);
18933             }, this);
18934             return;
18935         }
18936         var node = this.getNode(nodeInfo);
18937         if(!node || !this.isSelected(node)){
18938             //Roo.log("not selected");
18939             return; // not selected.
18940         }
18941         // fireevent???
18942         var ns = [];
18943         Roo.each(this.selections, function(s) {
18944             if (s == node ) {
18945                 Roo.fly(node).removeClass(this.selectedClass);
18946
18947                 return;
18948             }
18949             ns.push(s);
18950         },this);
18951         
18952         this.selections= ns;
18953         this.fireEvent("selectionchange", this, this.selections);
18954     },
18955
18956     /**
18957      * Gets a template node.
18958      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18959      * @return {HTMLElement} The node or null if it wasn't found
18960      */
18961     getNode : function(nodeInfo){
18962         if(typeof nodeInfo == "string"){
18963             return document.getElementById(nodeInfo);
18964         }else if(typeof nodeInfo == "number"){
18965             return this.nodes[nodeInfo];
18966         }
18967         return nodeInfo;
18968     },
18969
18970     /**
18971      * Gets a range template nodes.
18972      * @param {Number} startIndex
18973      * @param {Number} endIndex
18974      * @return {Array} An array of nodes
18975      */
18976     getNodes : function(start, end){
18977         var ns = this.nodes;
18978         start = start || 0;
18979         end = typeof end == "undefined" ? ns.length - 1 : end;
18980         var nodes = [];
18981         if(start <= end){
18982             for(var i = start; i <= end; i++){
18983                 nodes.push(ns[i]);
18984             }
18985         } else{
18986             for(var i = start; i >= end; i--){
18987                 nodes.push(ns[i]);
18988             }
18989         }
18990         return nodes;
18991     },
18992
18993     /**
18994      * Finds the index of the passed node
18995      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18996      * @return {Number} The index of the node or -1
18997      */
18998     indexOf : function(node){
18999         node = this.getNode(node);
19000         if(typeof node.nodeIndex == "number"){
19001             return node.nodeIndex;
19002         }
19003         var ns = this.nodes;
19004         for(var i = 0, len = ns.length; i < len; i++){
19005             if(ns[i] == node){
19006                 return i;
19007             }
19008         }
19009         return -1;
19010     }
19011 });
19012 /*
19013  * - LGPL
19014  *
19015  * based on jquery fullcalendar
19016  * 
19017  */
19018
19019 Roo.bootstrap = Roo.bootstrap || {};
19020 /**
19021  * @class Roo.bootstrap.Calendar
19022  * @extends Roo.bootstrap.Component
19023  * Bootstrap Calendar class
19024  * @cfg {Boolean} loadMask (true|false) default false
19025  * @cfg {Object} header generate the user specific header of the calendar, default false
19026
19027  * @constructor
19028  * Create a new Container
19029  * @param {Object} config The config object
19030  */
19031
19032
19033
19034 Roo.bootstrap.Calendar = function(config){
19035     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19036      this.addEvents({
19037         /**
19038              * @event select
19039              * Fires when a date is selected
19040              * @param {DatePicker} this
19041              * @param {Date} date The selected date
19042              */
19043         'select': true,
19044         /**
19045              * @event monthchange
19046              * Fires when the displayed month changes 
19047              * @param {DatePicker} this
19048              * @param {Date} date The selected month
19049              */
19050         'monthchange': true,
19051         /**
19052              * @event evententer
19053              * Fires when mouse over an event
19054              * @param {Calendar} this
19055              * @param {event} Event
19056              */
19057         'evententer': true,
19058         /**
19059              * @event eventleave
19060              * Fires when the mouse leaves an
19061              * @param {Calendar} this
19062              * @param {event}
19063              */
19064         'eventleave': true,
19065         /**
19066              * @event eventclick
19067              * Fires when the mouse click an
19068              * @param {Calendar} this
19069              * @param {event}
19070              */
19071         'eventclick': true
19072         
19073     });
19074
19075 };
19076
19077 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19078     
19079      /**
19080      * @cfg {Number} startDay
19081      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19082      */
19083     startDay : 0,
19084     
19085     loadMask : false,
19086     
19087     header : false,
19088       
19089     getAutoCreate : function(){
19090         
19091         
19092         var fc_button = function(name, corner, style, content ) {
19093             return Roo.apply({},{
19094                 tag : 'span',
19095                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19096                          (corner.length ?
19097                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19098                             ''
19099                         ),
19100                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19101                 unselectable: 'on'
19102             });
19103         };
19104         
19105         var header = {};
19106         
19107         if(!this.header){
19108             header = {
19109                 tag : 'table',
19110                 cls : 'fc-header',
19111                 style : 'width:100%',
19112                 cn : [
19113                     {
19114                         tag: 'tr',
19115                         cn : [
19116                             {
19117                                 tag : 'td',
19118                                 cls : 'fc-header-left',
19119                                 cn : [
19120                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19121                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19122                                     { tag: 'span', cls: 'fc-header-space' },
19123                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19124
19125
19126                                 ]
19127                             },
19128
19129                             {
19130                                 tag : 'td',
19131                                 cls : 'fc-header-center',
19132                                 cn : [
19133                                     {
19134                                         tag: 'span',
19135                                         cls: 'fc-header-title',
19136                                         cn : {
19137                                             tag: 'H2',
19138                                             html : 'month / year'
19139                                         }
19140                                     }
19141
19142                                 ]
19143                             },
19144                             {
19145                                 tag : 'td',
19146                                 cls : 'fc-header-right',
19147                                 cn : [
19148                               /*      fc_button('month', 'left', '', 'month' ),
19149                                     fc_button('week', '', '', 'week' ),
19150                                     fc_button('day', 'right', '', 'day' )
19151                                 */    
19152
19153                                 ]
19154                             }
19155
19156                         ]
19157                     }
19158                 ]
19159             };
19160         }
19161         
19162         header = this.header;
19163         
19164        
19165         var cal_heads = function() {
19166             var ret = [];
19167             // fixme - handle this.
19168             
19169             for (var i =0; i < Date.dayNames.length; i++) {
19170                 var d = Date.dayNames[i];
19171                 ret.push({
19172                     tag: 'th',
19173                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19174                     html : d.substring(0,3)
19175                 });
19176                 
19177             }
19178             ret[0].cls += ' fc-first';
19179             ret[6].cls += ' fc-last';
19180             return ret;
19181         };
19182         var cal_cell = function(n) {
19183             return  {
19184                 tag: 'td',
19185                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19186                 cn : [
19187                     {
19188                         cn : [
19189                             {
19190                                 cls: 'fc-day-number',
19191                                 html: 'D'
19192                             },
19193                             {
19194                                 cls: 'fc-day-content',
19195                              
19196                                 cn : [
19197                                      {
19198                                         style: 'position: relative;' // height: 17px;
19199                                     }
19200                                 ]
19201                             }
19202                             
19203                             
19204                         ]
19205                     }
19206                 ]
19207                 
19208             }
19209         };
19210         var cal_rows = function() {
19211             
19212             var ret = [];
19213             for (var r = 0; r < 6; r++) {
19214                 var row= {
19215                     tag : 'tr',
19216                     cls : 'fc-week',
19217                     cn : []
19218                 };
19219                 
19220                 for (var i =0; i < Date.dayNames.length; i++) {
19221                     var d = Date.dayNames[i];
19222                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19223
19224                 }
19225                 row.cn[0].cls+=' fc-first';
19226                 row.cn[0].cn[0].style = 'min-height:90px';
19227                 row.cn[6].cls+=' fc-last';
19228                 ret.push(row);
19229                 
19230             }
19231             ret[0].cls += ' fc-first';
19232             ret[4].cls += ' fc-prev-last';
19233             ret[5].cls += ' fc-last';
19234             return ret;
19235             
19236         };
19237         
19238         var cal_table = {
19239             tag: 'table',
19240             cls: 'fc-border-separate',
19241             style : 'width:100%',
19242             cellspacing  : 0,
19243             cn : [
19244                 { 
19245                     tag: 'thead',
19246                     cn : [
19247                         { 
19248                             tag: 'tr',
19249                             cls : 'fc-first fc-last',
19250                             cn : cal_heads()
19251                         }
19252                     ]
19253                 },
19254                 { 
19255                     tag: 'tbody',
19256                     cn : cal_rows()
19257                 }
19258                   
19259             ]
19260         };
19261          
19262          var cfg = {
19263             cls : 'fc fc-ltr',
19264             cn : [
19265                 header,
19266                 {
19267                     cls : 'fc-content',
19268                     style : "position: relative;",
19269                     cn : [
19270                         {
19271                             cls : 'fc-view fc-view-month fc-grid',
19272                             style : 'position: relative',
19273                             unselectable : 'on',
19274                             cn : [
19275                                 {
19276                                     cls : 'fc-event-container',
19277                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19278                                 },
19279                                 cal_table
19280                             ]
19281                         }
19282                     ]
19283     
19284                 }
19285            ] 
19286             
19287         };
19288         
19289          
19290         
19291         return cfg;
19292     },
19293     
19294     
19295     initEvents : function()
19296     {
19297         if(!this.store){
19298             throw "can not find store for calendar";
19299         }
19300         
19301         var mark = {
19302             tag: "div",
19303             cls:"x-dlg-mask",
19304             style: "text-align:center",
19305             cn: [
19306                 {
19307                     tag: "div",
19308                     style: "background-color:white;width:50%;margin:250 auto",
19309                     cn: [
19310                         {
19311                             tag: "img",
19312                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19313                         },
19314                         {
19315                             tag: "span",
19316                             html: "Loading"
19317                         }
19318                         
19319                     ]
19320                 }
19321             ]
19322         };
19323         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19324         
19325         var size = this.el.select('.fc-content', true).first().getSize();
19326         this.maskEl.setSize(size.width, size.height);
19327         this.maskEl.enableDisplayMode("block");
19328         if(!this.loadMask){
19329             this.maskEl.hide();
19330         }
19331         
19332         this.store = Roo.factory(this.store, Roo.data);
19333         this.store.on('load', this.onLoad, this);
19334         this.store.on('beforeload', this.onBeforeLoad, this);
19335         
19336         this.resize();
19337         
19338         this.cells = this.el.select('.fc-day',true);
19339         //Roo.log(this.cells);
19340         this.textNodes = this.el.query('.fc-day-number');
19341         this.cells.addClassOnOver('fc-state-hover');
19342         
19343         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19344         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19345         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19346         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19347         
19348         this.on('monthchange', this.onMonthChange, this);
19349         
19350         this.update(new Date().clearTime());
19351     },
19352     
19353     resize : function() {
19354         var sz  = this.el.getSize();
19355         
19356         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19357         this.el.select('.fc-day-content div',true).setHeight(34);
19358     },
19359     
19360     
19361     // private
19362     showPrevMonth : function(e){
19363         this.update(this.activeDate.add("mo", -1));
19364     },
19365     showToday : function(e){
19366         this.update(new Date().clearTime());
19367     },
19368     // private
19369     showNextMonth : function(e){
19370         this.update(this.activeDate.add("mo", 1));
19371     },
19372
19373     // private
19374     showPrevYear : function(){
19375         this.update(this.activeDate.add("y", -1));
19376     },
19377
19378     // private
19379     showNextYear : function(){
19380         this.update(this.activeDate.add("y", 1));
19381     },
19382
19383     
19384    // private
19385     update : function(date)
19386     {
19387         var vd = this.activeDate;
19388         this.activeDate = date;
19389 //        if(vd && this.el){
19390 //            var t = date.getTime();
19391 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19392 //                Roo.log('using add remove');
19393 //                
19394 //                this.fireEvent('monthchange', this, date);
19395 //                
19396 //                this.cells.removeClass("fc-state-highlight");
19397 //                this.cells.each(function(c){
19398 //                   if(c.dateValue == t){
19399 //                       c.addClass("fc-state-highlight");
19400 //                       setTimeout(function(){
19401 //                            try{c.dom.firstChild.focus();}catch(e){}
19402 //                       }, 50);
19403 //                       return false;
19404 //                   }
19405 //                   return true;
19406 //                });
19407 //                return;
19408 //            }
19409 //        }
19410         
19411         var days = date.getDaysInMonth();
19412         
19413         var firstOfMonth = date.getFirstDateOfMonth();
19414         var startingPos = firstOfMonth.getDay()-this.startDay;
19415         
19416         if(startingPos < this.startDay){
19417             startingPos += 7;
19418         }
19419         
19420         var pm = date.add(Date.MONTH, -1);
19421         var prevStart = pm.getDaysInMonth()-startingPos;
19422 //        
19423         this.cells = this.el.select('.fc-day',true);
19424         this.textNodes = this.el.query('.fc-day-number');
19425         this.cells.addClassOnOver('fc-state-hover');
19426         
19427         var cells = this.cells.elements;
19428         var textEls = this.textNodes;
19429         
19430         Roo.each(cells, function(cell){
19431             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19432         });
19433         
19434         days += startingPos;
19435
19436         // convert everything to numbers so it's fast
19437         var day = 86400000;
19438         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19439         //Roo.log(d);
19440         //Roo.log(pm);
19441         //Roo.log(prevStart);
19442         
19443         var today = new Date().clearTime().getTime();
19444         var sel = date.clearTime().getTime();
19445         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19446         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19447         var ddMatch = this.disabledDatesRE;
19448         var ddText = this.disabledDatesText;
19449         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19450         var ddaysText = this.disabledDaysText;
19451         var format = this.format;
19452         
19453         var setCellClass = function(cal, cell){
19454             cell.row = 0;
19455             cell.events = [];
19456             cell.more = [];
19457             //Roo.log('set Cell Class');
19458             cell.title = "";
19459             var t = d.getTime();
19460             
19461             //Roo.log(d);
19462             
19463             cell.dateValue = t;
19464             if(t == today){
19465                 cell.className += " fc-today";
19466                 cell.className += " fc-state-highlight";
19467                 cell.title = cal.todayText;
19468             }
19469             if(t == sel){
19470                 // disable highlight in other month..
19471                 //cell.className += " fc-state-highlight";
19472                 
19473             }
19474             // disabling
19475             if(t < min) {
19476                 cell.className = " fc-state-disabled";
19477                 cell.title = cal.minText;
19478                 return;
19479             }
19480             if(t > max) {
19481                 cell.className = " fc-state-disabled";
19482                 cell.title = cal.maxText;
19483                 return;
19484             }
19485             if(ddays){
19486                 if(ddays.indexOf(d.getDay()) != -1){
19487                     cell.title = ddaysText;
19488                     cell.className = " fc-state-disabled";
19489                 }
19490             }
19491             if(ddMatch && format){
19492                 var fvalue = d.dateFormat(format);
19493                 if(ddMatch.test(fvalue)){
19494                     cell.title = ddText.replace("%0", fvalue);
19495                     cell.className = " fc-state-disabled";
19496                 }
19497             }
19498             
19499             if (!cell.initialClassName) {
19500                 cell.initialClassName = cell.dom.className;
19501             }
19502             
19503             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19504         };
19505
19506         var i = 0;
19507         
19508         for(; i < startingPos; i++) {
19509             textEls[i].innerHTML = (++prevStart);
19510             d.setDate(d.getDate()+1);
19511             
19512             cells[i].className = "fc-past fc-other-month";
19513             setCellClass(this, cells[i]);
19514         }
19515         
19516         var intDay = 0;
19517         
19518         for(; i < days; i++){
19519             intDay = i - startingPos + 1;
19520             textEls[i].innerHTML = (intDay);
19521             d.setDate(d.getDate()+1);
19522             
19523             cells[i].className = ''; // "x-date-active";
19524             setCellClass(this, cells[i]);
19525         }
19526         var extraDays = 0;
19527         
19528         for(; i < 42; i++) {
19529             textEls[i].innerHTML = (++extraDays);
19530             d.setDate(d.getDate()+1);
19531             
19532             cells[i].className = "fc-future fc-other-month";
19533             setCellClass(this, cells[i]);
19534         }
19535         
19536         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19537         
19538         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19539         
19540         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19541         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19542         
19543         if(totalRows != 6){
19544             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19545             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19546         }
19547         
19548         this.fireEvent('monthchange', this, date);
19549         
19550         
19551         /*
19552         if(!this.internalRender){
19553             var main = this.el.dom.firstChild;
19554             var w = main.offsetWidth;
19555             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19556             Roo.fly(main).setWidth(w);
19557             this.internalRender = true;
19558             // opera does not respect the auto grow header center column
19559             // then, after it gets a width opera refuses to recalculate
19560             // without a second pass
19561             if(Roo.isOpera && !this.secondPass){
19562                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19563                 this.secondPass = true;
19564                 this.update.defer(10, this, [date]);
19565             }
19566         }
19567         */
19568         
19569     },
19570     
19571     findCell : function(dt) {
19572         dt = dt.clearTime().getTime();
19573         var ret = false;
19574         this.cells.each(function(c){
19575             //Roo.log("check " +c.dateValue + '?=' + dt);
19576             if(c.dateValue == dt){
19577                 ret = c;
19578                 return false;
19579             }
19580             return true;
19581         });
19582         
19583         return ret;
19584     },
19585     
19586     findCells : function(ev) {
19587         var s = ev.start.clone().clearTime().getTime();
19588        // Roo.log(s);
19589         var e= ev.end.clone().clearTime().getTime();
19590        // Roo.log(e);
19591         var ret = [];
19592         this.cells.each(function(c){
19593              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19594             
19595             if(c.dateValue > e){
19596                 return ;
19597             }
19598             if(c.dateValue < s){
19599                 return ;
19600             }
19601             ret.push(c);
19602         });
19603         
19604         return ret;    
19605     },
19606     
19607 //    findBestRow: function(cells)
19608 //    {
19609 //        var ret = 0;
19610 //        
19611 //        for (var i =0 ; i < cells.length;i++) {
19612 //            ret  = Math.max(cells[i].rows || 0,ret);
19613 //        }
19614 //        return ret;
19615 //        
19616 //    },
19617     
19618     
19619     addItem : function(ev)
19620     {
19621         // look for vertical location slot in
19622         var cells = this.findCells(ev);
19623         
19624 //        ev.row = this.findBestRow(cells);
19625         
19626         // work out the location.
19627         
19628         var crow = false;
19629         var rows = [];
19630         for(var i =0; i < cells.length; i++) {
19631             
19632             cells[i].row = cells[0].row;
19633             
19634             if(i == 0){
19635                 cells[i].row = cells[i].row + 1;
19636             }
19637             
19638             if (!crow) {
19639                 crow = {
19640                     start : cells[i],
19641                     end :  cells[i]
19642                 };
19643                 continue;
19644             }
19645             if (crow.start.getY() == cells[i].getY()) {
19646                 // on same row.
19647                 crow.end = cells[i];
19648                 continue;
19649             }
19650             // different row.
19651             rows.push(crow);
19652             crow = {
19653                 start: cells[i],
19654                 end : cells[i]
19655             };
19656             
19657         }
19658         
19659         rows.push(crow);
19660         ev.els = [];
19661         ev.rows = rows;
19662         ev.cells = cells;
19663         
19664         cells[0].events.push(ev);
19665         
19666         this.calevents.push(ev);
19667     },
19668     
19669     clearEvents: function() {
19670         
19671         if(!this.calevents){
19672             return;
19673         }
19674         
19675         Roo.each(this.cells.elements, function(c){
19676             c.row = 0;
19677             c.events = [];
19678             c.more = [];
19679         });
19680         
19681         Roo.each(this.calevents, function(e) {
19682             Roo.each(e.els, function(el) {
19683                 el.un('mouseenter' ,this.onEventEnter, this);
19684                 el.un('mouseleave' ,this.onEventLeave, this);
19685                 el.remove();
19686             },this);
19687         },this);
19688         
19689         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19690             e.remove();
19691         });
19692         
19693     },
19694     
19695     renderEvents: function()
19696     {   
19697         var _this = this;
19698         
19699         this.cells.each(function(c) {
19700             
19701             if(c.row < 5){
19702                 return;
19703             }
19704             
19705             var ev = c.events;
19706             
19707             var r = 4;
19708             if(c.row != c.events.length){
19709                 r = 4 - (4 - (c.row - c.events.length));
19710             }
19711             
19712             c.events = ev.slice(0, r);
19713             c.more = ev.slice(r);
19714             
19715             if(c.more.length && c.more.length == 1){
19716                 c.events.push(c.more.pop());
19717             }
19718             
19719             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19720             
19721         });
19722             
19723         this.cells.each(function(c) {
19724             
19725             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19726             
19727             
19728             for (var e = 0; e < c.events.length; e++){
19729                 var ev = c.events[e];
19730                 var rows = ev.rows;
19731                 
19732                 for(var i = 0; i < rows.length; i++) {
19733                 
19734                     // how many rows should it span..
19735
19736                     var  cfg = {
19737                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19738                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19739
19740                         unselectable : "on",
19741                         cn : [
19742                             {
19743                                 cls: 'fc-event-inner',
19744                                 cn : [
19745     //                                {
19746     //                                  tag:'span',
19747     //                                  cls: 'fc-event-time',
19748     //                                  html : cells.length > 1 ? '' : ev.time
19749     //                                },
19750                                     {
19751                                       tag:'span',
19752                                       cls: 'fc-event-title',
19753                                       html : String.format('{0}', ev.title)
19754                                     }
19755
19756
19757                                 ]
19758                             },
19759                             {
19760                                 cls: 'ui-resizable-handle ui-resizable-e',
19761                                 html : '&nbsp;&nbsp;&nbsp'
19762                             }
19763
19764                         ]
19765                     };
19766
19767                     if (i == 0) {
19768                         cfg.cls += ' fc-event-start';
19769                     }
19770                     if ((i+1) == rows.length) {
19771                         cfg.cls += ' fc-event-end';
19772                     }
19773
19774                     var ctr = _this.el.select('.fc-event-container',true).first();
19775                     var cg = ctr.createChild(cfg);
19776
19777                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19778                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19779
19780                     var r = (c.more.length) ? 1 : 0;
19781                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19782                     cg.setWidth(ebox.right - sbox.x -2);
19783
19784                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19785                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19786                     cg.on('click', _this.onEventClick, _this, ev);
19787
19788                     ev.els.push(cg);
19789                     
19790                 }
19791                 
19792             }
19793             
19794             
19795             if(c.more.length){
19796                 var  cfg = {
19797                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19798                     style : 'position: absolute',
19799                     unselectable : "on",
19800                     cn : [
19801                         {
19802                             cls: 'fc-event-inner',
19803                             cn : [
19804                                 {
19805                                   tag:'span',
19806                                   cls: 'fc-event-title',
19807                                   html : 'More'
19808                                 }
19809
19810
19811                             ]
19812                         },
19813                         {
19814                             cls: 'ui-resizable-handle ui-resizable-e',
19815                             html : '&nbsp;&nbsp;&nbsp'
19816                         }
19817
19818                     ]
19819                 };
19820
19821                 var ctr = _this.el.select('.fc-event-container',true).first();
19822                 var cg = ctr.createChild(cfg);
19823
19824                 var sbox = c.select('.fc-day-content',true).first().getBox();
19825                 var ebox = c.select('.fc-day-content',true).first().getBox();
19826                 //Roo.log(cg);
19827                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19828                 cg.setWidth(ebox.right - sbox.x -2);
19829
19830                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19831                 
19832             }
19833             
19834         });
19835         
19836         
19837         
19838     },
19839     
19840     onEventEnter: function (e, el,event,d) {
19841         this.fireEvent('evententer', this, el, event);
19842     },
19843     
19844     onEventLeave: function (e, el,event,d) {
19845         this.fireEvent('eventleave', this, el, event);
19846     },
19847     
19848     onEventClick: function (e, el,event,d) {
19849         this.fireEvent('eventclick', this, el, event);
19850     },
19851     
19852     onMonthChange: function () {
19853         this.store.load();
19854     },
19855     
19856     onMoreEventClick: function(e, el, more)
19857     {
19858         var _this = this;
19859         
19860         this.calpopover.placement = 'right';
19861         this.calpopover.setTitle('More');
19862         
19863         this.calpopover.setContent('');
19864         
19865         var ctr = this.calpopover.el.select('.popover-content', true).first();
19866         
19867         Roo.each(more, function(m){
19868             var cfg = {
19869                 cls : 'fc-event-hori fc-event-draggable',
19870                 html : m.title
19871             };
19872             var cg = ctr.createChild(cfg);
19873             
19874             cg.on('click', _this.onEventClick, _this, m);
19875         });
19876         
19877         this.calpopover.show(el);
19878         
19879         
19880     },
19881     
19882     onLoad: function () 
19883     {   
19884         this.calevents = [];
19885         var cal = this;
19886         
19887         if(this.store.getCount() > 0){
19888             this.store.data.each(function(d){
19889                cal.addItem({
19890                     id : d.data.id,
19891                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19892                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19893                     time : d.data.start_time,
19894                     title : d.data.title,
19895                     description : d.data.description,
19896                     venue : d.data.venue
19897                 });
19898             });
19899         }
19900         
19901         this.renderEvents();
19902         
19903         if(this.calevents.length && this.loadMask){
19904             this.maskEl.hide();
19905         }
19906     },
19907     
19908     onBeforeLoad: function()
19909     {
19910         this.clearEvents();
19911         if(this.loadMask){
19912             this.maskEl.show();
19913         }
19914     }
19915 });
19916
19917  
19918  /*
19919  * - LGPL
19920  *
19921  * element
19922  * 
19923  */
19924
19925 /**
19926  * @class Roo.bootstrap.Popover
19927  * @extends Roo.bootstrap.Component
19928  * Bootstrap Popover class
19929  * @cfg {String} html contents of the popover   (or false to use children..)
19930  * @cfg {String} title of popover (or false to hide)
19931  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19932  * @cfg {String} trigger click || hover (or false to trigger manually)
19933  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19934  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19935  *      - if false and it has a 'parent' then it will be automatically added to that element
19936  *      - if string - Roo.get  will be called 
19937  * @cfg {Number} delay - delay before showing
19938  
19939  * @constructor
19940  * Create a new Popover
19941  * @param {Object} config The config object
19942  */
19943
19944 Roo.bootstrap.Popover = function(config){
19945     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19946     
19947     this.addEvents({
19948         // raw events
19949          /**
19950          * @event show
19951          * After the popover show
19952          * 
19953          * @param {Roo.bootstrap.Popover} this
19954          */
19955         "show" : true,
19956         /**
19957          * @event hide
19958          * After the popover hide
19959          * 
19960          * @param {Roo.bootstrap.Popover} this
19961          */
19962         "hide" : true
19963     });
19964 };
19965
19966 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19967     
19968     title: false,
19969     html: false,
19970     
19971     placement : 'right',
19972     trigger : 'hover', // hover
19973     modal : false,
19974     delay : 0,
19975     
19976     over: false,
19977     
19978     can_build_overlaid : false,
19979     
19980     maskEl : false, // the mask element
19981     headerEl : false,
19982     contentEl : false,
19983     alignEl : false, // when show is called with an element - this get's stored.
19984     
19985     getChildContainer : function()
19986     {
19987         return this.contentEl;
19988         
19989     },
19990     getPopoverHeader : function()
19991     {
19992         this.title = true; // flag not to hide it..
19993         this.headerEl.addClass('p-0');
19994         return this.headerEl
19995     },
19996     
19997     
19998     getAutoCreate : function(){
19999          
20000         var cfg = {
20001            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20002            style: 'display:block',
20003            cn : [
20004                 {
20005                     cls : 'arrow'
20006                 },
20007                 {
20008                     cls : 'popover-inner ',
20009                     cn : [
20010                         {
20011                             tag: 'h3',
20012                             cls: 'popover-title popover-header',
20013                             html : this.title === false ? '' : this.title
20014                         },
20015                         {
20016                             cls : 'popover-content popover-body '  + (this.cls || ''),
20017                             html : this.html || ''
20018                         }
20019                     ]
20020                     
20021                 }
20022            ]
20023         };
20024         
20025         return cfg;
20026     },
20027     /**
20028      * @param {string} the title
20029      */
20030     setTitle: function(str)
20031     {
20032         this.title = str;
20033         if (this.el) {
20034             this.headerEl.dom.innerHTML = str;
20035         }
20036         
20037     },
20038     /**
20039      * @param {string} the body content
20040      */
20041     setContent: function(str)
20042     {
20043         this.html = str;
20044         if (this.contentEl) {
20045             this.contentEl.dom.innerHTML = str;
20046         }
20047         
20048     },
20049     // as it get's added to the bottom of the page.
20050     onRender : function(ct, position)
20051     {
20052         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20053         
20054         
20055         
20056         if(!this.el){
20057             var cfg = Roo.apply({},  this.getAutoCreate());
20058             cfg.id = Roo.id();
20059             
20060             if (this.cls) {
20061                 cfg.cls += ' ' + this.cls;
20062             }
20063             if (this.style) {
20064                 cfg.style = this.style;
20065             }
20066             //Roo.log("adding to ");
20067             this.el = Roo.get(document.body).createChild(cfg, position);
20068 //            Roo.log(this.el);
20069         }
20070         
20071         this.contentEl = this.el.select('.popover-content',true).first();
20072         this.headerEl =  this.el.select('.popover-title',true).first();
20073         
20074         var nitems = [];
20075         if(typeof(this.items) != 'undefined'){
20076             var items = this.items;
20077             delete this.items;
20078
20079             for(var i =0;i < items.length;i++) {
20080                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20081             }
20082         }
20083
20084         this.items = nitems;
20085         
20086         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20087         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20088         
20089         
20090         
20091         this.initEvents();
20092     },
20093     
20094     resizeMask : function()
20095     {
20096         this.maskEl.setSize(
20097             Roo.lib.Dom.getViewWidth(true),
20098             Roo.lib.Dom.getViewHeight(true)
20099         );
20100     },
20101     
20102     initEvents : function()
20103     {
20104         
20105         if (!this.modal) { 
20106             Roo.bootstrap.Popover.register(this);
20107         }
20108          
20109         this.arrowEl = this.el.select('.arrow',true).first();
20110         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20111         this.el.enableDisplayMode('block');
20112         this.el.hide();
20113  
20114         
20115         if (this.over === false && !this.parent()) {
20116             return; 
20117         }
20118         if (this.triggers === false) {
20119             return;
20120         }
20121          
20122         // support parent
20123         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20124         var triggers = this.trigger ? this.trigger.split(' ') : [];
20125         Roo.each(triggers, function(trigger) {
20126         
20127             if (trigger == 'click') {
20128                 on_el.on('click', this.toggle, this);
20129             } else if (trigger != 'manual') {
20130                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20131                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20132       
20133                 on_el.on(eventIn  ,this.enter, this);
20134                 on_el.on(eventOut, this.leave, this);
20135             }
20136         }, this);
20137     },
20138     
20139     
20140     // private
20141     timeout : null,
20142     hoverState : null,
20143     
20144     toggle : function () {
20145         this.hoverState == 'in' ? this.leave() : this.enter();
20146     },
20147     
20148     enter : function () {
20149         
20150         clearTimeout(this.timeout);
20151     
20152         this.hoverState = 'in';
20153     
20154         if (!this.delay || !this.delay.show) {
20155             this.show();
20156             return;
20157         }
20158         var _t = this;
20159         this.timeout = setTimeout(function () {
20160             if (_t.hoverState == 'in') {
20161                 _t.show();
20162             }
20163         }, this.delay.show)
20164     },
20165     
20166     leave : function() {
20167         clearTimeout(this.timeout);
20168     
20169         this.hoverState = 'out';
20170     
20171         if (!this.delay || !this.delay.hide) {
20172             this.hide();
20173             return;
20174         }
20175         var _t = this;
20176         this.timeout = setTimeout(function () {
20177             if (_t.hoverState == 'out') {
20178                 _t.hide();
20179             }
20180         }, this.delay.hide)
20181     },
20182     /**
20183      * Show the popover
20184      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20185      * @param {string} (left|right|top|bottom) position
20186      */
20187     show : function (on_el, placement)
20188     {
20189         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20190         on_el = on_el || false; // default to false
20191          
20192         if (!on_el) {
20193             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20194                 on_el = this.parent().el;
20195             } else if (this.over) {
20196                 Roo.get(this.over);
20197             }
20198             
20199         }
20200         
20201         this.alignEl = Roo.get( on_el );
20202
20203         if (!this.el) {
20204             this.render(document.body);
20205         }
20206         
20207         
20208          
20209         
20210         if (this.title === false) {
20211             this.headerEl.hide();
20212         }
20213         
20214        
20215         this.el.show();
20216         this.el.dom.style.display = 'block';
20217          
20218  
20219         if (this.alignEl) {
20220             this.updatePosition(this.placement, true);
20221              
20222         } else {
20223             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20224             var es = this.el.getSize();
20225             var x = Roo.lib.Dom.getViewWidth()/2;
20226             var y = Roo.lib.Dom.getViewHeight()/2;
20227             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20228             
20229         }
20230
20231         
20232         //var arrow = this.el.select('.arrow',true).first();
20233         //arrow.set(align[2], 
20234         
20235         this.el.addClass('in');
20236         
20237          
20238         
20239         this.hoverState = 'in';
20240         
20241         if (this.modal) {
20242             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20243             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20244             this.maskEl.dom.style.display = 'block';
20245             this.maskEl.addClass('show');
20246         }
20247         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20248  
20249         this.fireEvent('show', this);
20250         
20251     },
20252     /**
20253      * fire this manually after loading a grid in the table for example
20254      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20255      * @param {Boolean} try and move it if we cant get right position.
20256      */
20257     updatePosition : function(placement, try_move)
20258     {
20259         // allow for calling with no parameters
20260         placement = placement   ? placement :  this.placement;
20261         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20262         
20263         this.el.removeClass([
20264             'fade','top','bottom', 'left', 'right','in',
20265             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20266         ]);
20267         this.el.addClass(placement + ' bs-popover-' + placement);
20268         
20269         if (!this.alignEl ) {
20270             return false;
20271         }
20272         
20273         switch (placement) {
20274             case 'right':
20275                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20276                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20277                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20278                     //normal display... or moved up/down.
20279                     this.el.setXY(offset);
20280                     var xy = this.alignEl.getAnchorXY('tr', false);
20281                     xy[0]+=2;xy[1]+=5;
20282                     this.arrowEl.setXY(xy);
20283                     return true;
20284                 }
20285                 // continue through...
20286                 return this.updatePosition('left', false);
20287                 
20288             
20289             case 'left':
20290                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20291                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20292                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20293                     //normal display... or moved up/down.
20294                     this.el.setXY(offset);
20295                     var xy = this.alignEl.getAnchorXY('tl', false);
20296                     xy[0]-=10;xy[1]+=5; // << fix me
20297                     this.arrowEl.setXY(xy);
20298                     return true;
20299                 }
20300                 // call self...
20301                 return this.updatePosition('right', false);
20302             
20303             case 'top':
20304                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20305                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20306                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20307                     //normal display... or moved up/down.
20308                     this.el.setXY(offset);
20309                     var xy = this.alignEl.getAnchorXY('t', false);
20310                     xy[1]-=10; // << fix me
20311                     this.arrowEl.setXY(xy);
20312                     return true;
20313                 }
20314                 // fall through
20315                return this.updatePosition('bottom', false);
20316             
20317             case 'bottom':
20318                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20319                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20320                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20321                     //normal display... or moved up/down.
20322                     this.el.setXY(offset);
20323                     var xy = this.alignEl.getAnchorXY('b', false);
20324                      xy[1]+=2; // << fix me
20325                     this.arrowEl.setXY(xy);
20326                     return true;
20327                 }
20328                 // fall through
20329                 return this.updatePosition('top', false);
20330                 
20331             
20332         }
20333         
20334         
20335         return false;
20336     },
20337     
20338     hide : function()
20339     {
20340         this.el.setXY([0,0]);
20341         this.el.removeClass('in');
20342         this.el.hide();
20343         this.hoverState = null;
20344         this.maskEl.hide(); // always..
20345         this.fireEvent('hide', this);
20346     }
20347     
20348 });
20349
20350
20351 Roo.apply(Roo.bootstrap.Popover, {
20352
20353     alignment : {
20354         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20355         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20356         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20357         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20358     },
20359     
20360     zIndex : 20001,
20361
20362     clickHander : false,
20363     
20364
20365     onMouseDown : function(e)
20366     {
20367         if (!e.getTarget(".roo-popover")) {
20368             this.hideAll();
20369         }
20370          
20371     },
20372     
20373     popups : [],
20374     
20375     register : function(popup)
20376     {
20377         if (!Roo.bootstrap.Popover.clickHandler) {
20378             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20379         }
20380         // hide other popups.
20381         this.hideAll();
20382         this.popups.push(popup);
20383     },
20384     hideAll : function()
20385     {
20386         this.popups.forEach(function(p) {
20387             p.hide();
20388         });
20389     }
20390
20391 });/*
20392  * - LGPL
20393  *
20394  * Card header - holder for the card header elements.
20395  * 
20396  */
20397
20398 /**
20399  * @class Roo.bootstrap.PopoverNav
20400  * @extends Roo.bootstrap.NavGroup
20401  * Bootstrap Popover header navigation class
20402  * @constructor
20403  * Create a new Popover Header Navigation 
20404  * @param {Object} config The config object
20405  */
20406
20407 Roo.bootstrap.PopoverNav = function(config){
20408     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20409 };
20410
20411 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20412     
20413     
20414     container_method : 'getPopoverHeader' 
20415     
20416      
20417     
20418     
20419    
20420 });
20421
20422  
20423
20424  /*
20425  * - LGPL
20426  *
20427  * Progress
20428  * 
20429  */
20430
20431 /**
20432  * @class Roo.bootstrap.Progress
20433  * @extends Roo.bootstrap.Component
20434  * Bootstrap Progress class
20435  * @cfg {Boolean} striped striped of the progress bar
20436  * @cfg {Boolean} active animated of the progress bar
20437  * 
20438  * 
20439  * @constructor
20440  * Create a new Progress
20441  * @param {Object} config The config object
20442  */
20443
20444 Roo.bootstrap.Progress = function(config){
20445     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20446 };
20447
20448 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20449     
20450     striped : false,
20451     active: false,
20452     
20453     getAutoCreate : function(){
20454         var cfg = {
20455             tag: 'div',
20456             cls: 'progress'
20457         };
20458         
20459         
20460         if(this.striped){
20461             cfg.cls += ' progress-striped';
20462         }
20463       
20464         if(this.active){
20465             cfg.cls += ' active';
20466         }
20467         
20468         
20469         return cfg;
20470     }
20471    
20472 });
20473
20474  
20475
20476  /*
20477  * - LGPL
20478  *
20479  * ProgressBar
20480  * 
20481  */
20482
20483 /**
20484  * @class Roo.bootstrap.ProgressBar
20485  * @extends Roo.bootstrap.Component
20486  * Bootstrap ProgressBar class
20487  * @cfg {Number} aria_valuenow aria-value now
20488  * @cfg {Number} aria_valuemin aria-value min
20489  * @cfg {Number} aria_valuemax aria-value max
20490  * @cfg {String} label label for the progress bar
20491  * @cfg {String} panel (success | info | warning | danger )
20492  * @cfg {String} role role of the progress bar
20493  * @cfg {String} sr_only text
20494  * 
20495  * 
20496  * @constructor
20497  * Create a new ProgressBar
20498  * @param {Object} config The config object
20499  */
20500
20501 Roo.bootstrap.ProgressBar = function(config){
20502     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20503 };
20504
20505 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20506     
20507     aria_valuenow : 0,
20508     aria_valuemin : 0,
20509     aria_valuemax : 100,
20510     label : false,
20511     panel : false,
20512     role : false,
20513     sr_only: false,
20514     
20515     getAutoCreate : function()
20516     {
20517         
20518         var cfg = {
20519             tag: 'div',
20520             cls: 'progress-bar',
20521             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20522         };
20523         
20524         if(this.sr_only){
20525             cfg.cn = {
20526                 tag: 'span',
20527                 cls: 'sr-only',
20528                 html: this.sr_only
20529             }
20530         }
20531         
20532         if(this.role){
20533             cfg.role = this.role;
20534         }
20535         
20536         if(this.aria_valuenow){
20537             cfg['aria-valuenow'] = this.aria_valuenow;
20538         }
20539         
20540         if(this.aria_valuemin){
20541             cfg['aria-valuemin'] = this.aria_valuemin;
20542         }
20543         
20544         if(this.aria_valuemax){
20545             cfg['aria-valuemax'] = this.aria_valuemax;
20546         }
20547         
20548         if(this.label && !this.sr_only){
20549             cfg.html = this.label;
20550         }
20551         
20552         if(this.panel){
20553             cfg.cls += ' progress-bar-' + this.panel;
20554         }
20555         
20556         return cfg;
20557     },
20558     
20559     update : function(aria_valuenow)
20560     {
20561         this.aria_valuenow = aria_valuenow;
20562         
20563         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20564     }
20565    
20566 });
20567
20568  
20569
20570  /*
20571  * - LGPL
20572  *
20573  * column
20574  * 
20575  */
20576
20577 /**
20578  * @class Roo.bootstrap.TabGroup
20579  * @extends Roo.bootstrap.Column
20580  * Bootstrap Column class
20581  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20582  * @cfg {Boolean} carousel true to make the group behave like a carousel
20583  * @cfg {Boolean} bullets show bullets for the panels
20584  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20585  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20586  * @cfg {Boolean} showarrow (true|false) show arrow default true
20587  * 
20588  * @constructor
20589  * Create a new TabGroup
20590  * @param {Object} config The config object
20591  */
20592
20593 Roo.bootstrap.TabGroup = function(config){
20594     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20595     if (!this.navId) {
20596         this.navId = Roo.id();
20597     }
20598     this.tabs = [];
20599     Roo.bootstrap.TabGroup.register(this);
20600     
20601 };
20602
20603 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20604     
20605     carousel : false,
20606     transition : false,
20607     bullets : 0,
20608     timer : 0,
20609     autoslide : false,
20610     slideFn : false,
20611     slideOnTouch : false,
20612     showarrow : true,
20613     
20614     getAutoCreate : function()
20615     {
20616         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20617         
20618         cfg.cls += ' tab-content';
20619         
20620         if (this.carousel) {
20621             cfg.cls += ' carousel slide';
20622             
20623             cfg.cn = [{
20624                cls : 'carousel-inner',
20625                cn : []
20626             }];
20627         
20628             if(this.bullets  && !Roo.isTouch){
20629                 
20630                 var bullets = {
20631                     cls : 'carousel-bullets',
20632                     cn : []
20633                 };
20634                
20635                 if(this.bullets_cls){
20636                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20637                 }
20638                 
20639                 bullets.cn.push({
20640                     cls : 'clear'
20641                 });
20642                 
20643                 cfg.cn[0].cn.push(bullets);
20644             }
20645             
20646             if(this.showarrow){
20647                 cfg.cn[0].cn.push({
20648                     tag : 'div',
20649                     class : 'carousel-arrow',
20650                     cn : [
20651                         {
20652                             tag : 'div',
20653                             class : 'carousel-prev',
20654                             cn : [
20655                                 {
20656                                     tag : 'i',
20657                                     class : 'fa fa-chevron-left'
20658                                 }
20659                             ]
20660                         },
20661                         {
20662                             tag : 'div',
20663                             class : 'carousel-next',
20664                             cn : [
20665                                 {
20666                                     tag : 'i',
20667                                     class : 'fa fa-chevron-right'
20668                                 }
20669                             ]
20670                         }
20671                     ]
20672                 });
20673             }
20674             
20675         }
20676         
20677         return cfg;
20678     },
20679     
20680     initEvents:  function()
20681     {
20682 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20683 //            this.el.on("touchstart", this.onTouchStart, this);
20684 //        }
20685         
20686         if(this.autoslide){
20687             var _this = this;
20688             
20689             this.slideFn = window.setInterval(function() {
20690                 _this.showPanelNext();
20691             }, this.timer);
20692         }
20693         
20694         if(this.showarrow){
20695             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20696             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20697         }
20698         
20699         
20700     },
20701     
20702 //    onTouchStart : function(e, el, o)
20703 //    {
20704 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20705 //            return;
20706 //        }
20707 //        
20708 //        this.showPanelNext();
20709 //    },
20710     
20711     
20712     getChildContainer : function()
20713     {
20714         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20715     },
20716     
20717     /**
20718     * register a Navigation item
20719     * @param {Roo.bootstrap.NavItem} the navitem to add
20720     */
20721     register : function(item)
20722     {
20723         this.tabs.push( item);
20724         item.navId = this.navId; // not really needed..
20725         this.addBullet();
20726     
20727     },
20728     
20729     getActivePanel : function()
20730     {
20731         var r = false;
20732         Roo.each(this.tabs, function(t) {
20733             if (t.active) {
20734                 r = t;
20735                 return false;
20736             }
20737             return null;
20738         });
20739         return r;
20740         
20741     },
20742     getPanelByName : function(n)
20743     {
20744         var r = false;
20745         Roo.each(this.tabs, function(t) {
20746             if (t.tabId == n) {
20747                 r = t;
20748                 return false;
20749             }
20750             return null;
20751         });
20752         return r;
20753     },
20754     indexOfPanel : function(p)
20755     {
20756         var r = false;
20757         Roo.each(this.tabs, function(t,i) {
20758             if (t.tabId == p.tabId) {
20759                 r = i;
20760                 return false;
20761             }
20762             return null;
20763         });
20764         return r;
20765     },
20766     /**
20767      * show a specific panel
20768      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20769      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20770      */
20771     showPanel : function (pan)
20772     {
20773         if(this.transition || typeof(pan) == 'undefined'){
20774             Roo.log("waiting for the transitionend");
20775             return false;
20776         }
20777         
20778         if (typeof(pan) == 'number') {
20779             pan = this.tabs[pan];
20780         }
20781         
20782         if (typeof(pan) == 'string') {
20783             pan = this.getPanelByName(pan);
20784         }
20785         
20786         var cur = this.getActivePanel();
20787         
20788         if(!pan || !cur){
20789             Roo.log('pan or acitve pan is undefined');
20790             return false;
20791         }
20792         
20793         if (pan.tabId == this.getActivePanel().tabId) {
20794             return true;
20795         }
20796         
20797         if (false === cur.fireEvent('beforedeactivate')) {
20798             return false;
20799         }
20800         
20801         if(this.bullets > 0 && !Roo.isTouch){
20802             this.setActiveBullet(this.indexOfPanel(pan));
20803         }
20804         
20805         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20806             
20807             //class="carousel-item carousel-item-next carousel-item-left"
20808             
20809             this.transition = true;
20810             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20811             var lr = dir == 'next' ? 'left' : 'right';
20812             pan.el.addClass(dir); // or prev
20813             pan.el.addClass('carousel-item-' + dir); // or prev
20814             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20815             cur.el.addClass(lr); // or right
20816             pan.el.addClass(lr);
20817             cur.el.addClass('carousel-item-' +lr); // or right
20818             pan.el.addClass('carousel-item-' +lr);
20819             
20820             
20821             var _this = this;
20822             cur.el.on('transitionend', function() {
20823                 Roo.log("trans end?");
20824                 
20825                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20826                 pan.setActive(true);
20827                 
20828                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20829                 cur.setActive(false);
20830                 
20831                 _this.transition = false;
20832                 
20833             }, this, { single:  true } );
20834             
20835             return true;
20836         }
20837         
20838         cur.setActive(false);
20839         pan.setActive(true);
20840         
20841         return true;
20842         
20843     },
20844     showPanelNext : function()
20845     {
20846         var i = this.indexOfPanel(this.getActivePanel());
20847         
20848         if (i >= this.tabs.length - 1 && !this.autoslide) {
20849             return;
20850         }
20851         
20852         if (i >= this.tabs.length - 1 && this.autoslide) {
20853             i = -1;
20854         }
20855         
20856         this.showPanel(this.tabs[i+1]);
20857     },
20858     
20859     showPanelPrev : function()
20860     {
20861         var i = this.indexOfPanel(this.getActivePanel());
20862         
20863         if (i  < 1 && !this.autoslide) {
20864             return;
20865         }
20866         
20867         if (i < 1 && this.autoslide) {
20868             i = this.tabs.length;
20869         }
20870         
20871         this.showPanel(this.tabs[i-1]);
20872     },
20873     
20874     
20875     addBullet: function()
20876     {
20877         if(!this.bullets || Roo.isTouch){
20878             return;
20879         }
20880         var ctr = this.el.select('.carousel-bullets',true).first();
20881         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20882         var bullet = ctr.createChild({
20883             cls : 'bullet bullet-' + i
20884         },ctr.dom.lastChild);
20885         
20886         
20887         var _this = this;
20888         
20889         bullet.on('click', (function(e, el, o, ii, t){
20890
20891             e.preventDefault();
20892
20893             this.showPanel(ii);
20894
20895             if(this.autoslide && this.slideFn){
20896                 clearInterval(this.slideFn);
20897                 this.slideFn = window.setInterval(function() {
20898                     _this.showPanelNext();
20899                 }, this.timer);
20900             }
20901
20902         }).createDelegate(this, [i, bullet], true));
20903                 
20904         
20905     },
20906      
20907     setActiveBullet : function(i)
20908     {
20909         if(Roo.isTouch){
20910             return;
20911         }
20912         
20913         Roo.each(this.el.select('.bullet', true).elements, function(el){
20914             el.removeClass('selected');
20915         });
20916
20917         var bullet = this.el.select('.bullet-' + i, true).first();
20918         
20919         if(!bullet){
20920             return;
20921         }
20922         
20923         bullet.addClass('selected');
20924     }
20925     
20926     
20927   
20928 });
20929
20930  
20931
20932  
20933  
20934 Roo.apply(Roo.bootstrap.TabGroup, {
20935     
20936     groups: {},
20937      /**
20938     * register a Navigation Group
20939     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20940     */
20941     register : function(navgrp)
20942     {
20943         this.groups[navgrp.navId] = navgrp;
20944         
20945     },
20946     /**
20947     * fetch a Navigation Group based on the navigation ID
20948     * if one does not exist , it will get created.
20949     * @param {string} the navgroup to add
20950     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20951     */
20952     get: function(navId) {
20953         if (typeof(this.groups[navId]) == 'undefined') {
20954             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20955         }
20956         return this.groups[navId] ;
20957     }
20958     
20959     
20960     
20961 });
20962
20963  /*
20964  * - LGPL
20965  *
20966  * TabPanel
20967  * 
20968  */
20969
20970 /**
20971  * @class Roo.bootstrap.TabPanel
20972  * @extends Roo.bootstrap.Component
20973  * Bootstrap TabPanel class
20974  * @cfg {Boolean} active panel active
20975  * @cfg {String} html panel content
20976  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20977  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20978  * @cfg {String} href click to link..
20979  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20980  * 
20981  * 
20982  * @constructor
20983  * Create a new TabPanel
20984  * @param {Object} config The config object
20985  */
20986
20987 Roo.bootstrap.TabPanel = function(config){
20988     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20989     this.addEvents({
20990         /**
20991              * @event changed
20992              * Fires when the active status changes
20993              * @param {Roo.bootstrap.TabPanel} this
20994              * @param {Boolean} state the new state
20995             
20996          */
20997         'changed': true,
20998         /**
20999              * @event beforedeactivate
21000              * Fires before a tab is de-activated - can be used to do validation on a form.
21001              * @param {Roo.bootstrap.TabPanel} this
21002              * @return {Boolean} false if there is an error
21003             
21004          */
21005         'beforedeactivate': true
21006      });
21007     
21008     this.tabId = this.tabId || Roo.id();
21009   
21010 };
21011
21012 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21013     
21014     active: false,
21015     html: false,
21016     tabId: false,
21017     navId : false,
21018     href : '',
21019     touchSlide : false,
21020     getAutoCreate : function(){
21021         
21022         
21023         var cfg = {
21024             tag: 'div',
21025             // item is needed for carousel - not sure if it has any effect otherwise
21026             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21027             html: this.html || ''
21028         };
21029         
21030         if(this.active){
21031             cfg.cls += ' active';
21032         }
21033         
21034         if(this.tabId){
21035             cfg.tabId = this.tabId;
21036         }
21037         
21038         
21039         
21040         return cfg;
21041     },
21042     
21043     initEvents:  function()
21044     {
21045         var p = this.parent();
21046         
21047         this.navId = this.navId || p.navId;
21048         
21049         if (typeof(this.navId) != 'undefined') {
21050             // not really needed.. but just in case.. parent should be a NavGroup.
21051             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21052             
21053             tg.register(this);
21054             
21055             var i = tg.tabs.length - 1;
21056             
21057             if(this.active && tg.bullets > 0 && i < tg.bullets){
21058                 tg.setActiveBullet(i);
21059             }
21060         }
21061         
21062         this.el.on('click', this.onClick, this);
21063         
21064         if(Roo.isTouch && this.touchSlide){
21065             this.el.on("touchstart", this.onTouchStart, this);
21066             this.el.on("touchmove", this.onTouchMove, this);
21067             this.el.on("touchend", this.onTouchEnd, this);
21068         }
21069         
21070     },
21071     
21072     onRender : function(ct, position)
21073     {
21074         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21075     },
21076     
21077     setActive : function(state)
21078     {
21079         Roo.log("panel - set active " + this.tabId + "=" + state);
21080         
21081         this.active = state;
21082         if (!state) {
21083             this.el.removeClass('active');
21084             
21085         } else  if (!this.el.hasClass('active')) {
21086             this.el.addClass('active');
21087         }
21088         
21089         this.fireEvent('changed', this, state);
21090     },
21091     
21092     onClick : function(e)
21093     {
21094         e.preventDefault();
21095         
21096         if(!this.href.length){
21097             return;
21098         }
21099         
21100         window.location.href = this.href;
21101     },
21102     
21103     startX : 0,
21104     startY : 0,
21105     endX : 0,
21106     endY : 0,
21107     swiping : false,
21108     
21109     onTouchStart : function(e)
21110     {
21111         this.swiping = false;
21112         
21113         this.startX = e.browserEvent.touches[0].clientX;
21114         this.startY = e.browserEvent.touches[0].clientY;
21115     },
21116     
21117     onTouchMove : function(e)
21118     {
21119         this.swiping = true;
21120         
21121         this.endX = e.browserEvent.touches[0].clientX;
21122         this.endY = e.browserEvent.touches[0].clientY;
21123     },
21124     
21125     onTouchEnd : function(e)
21126     {
21127         if(!this.swiping){
21128             this.onClick(e);
21129             return;
21130         }
21131         
21132         var tabGroup = this.parent();
21133         
21134         if(this.endX > this.startX){ // swiping right
21135             tabGroup.showPanelPrev();
21136             return;
21137         }
21138         
21139         if(this.startX > this.endX){ // swiping left
21140             tabGroup.showPanelNext();
21141             return;
21142         }
21143     }
21144     
21145     
21146 });
21147  
21148
21149  
21150
21151  /*
21152  * - LGPL
21153  *
21154  * DateField
21155  * 
21156  */
21157
21158 /**
21159  * @class Roo.bootstrap.DateField
21160  * @extends Roo.bootstrap.Input
21161  * Bootstrap DateField class
21162  * @cfg {Number} weekStart default 0
21163  * @cfg {String} viewMode default empty, (months|years)
21164  * @cfg {String} minViewMode default empty, (months|years)
21165  * @cfg {Number} startDate default -Infinity
21166  * @cfg {Number} endDate default Infinity
21167  * @cfg {Boolean} todayHighlight default false
21168  * @cfg {Boolean} todayBtn default false
21169  * @cfg {Boolean} calendarWeeks default false
21170  * @cfg {Object} daysOfWeekDisabled default empty
21171  * @cfg {Boolean} singleMode default false (true | false)
21172  * 
21173  * @cfg {Boolean} keyboardNavigation default true
21174  * @cfg {String} language default en
21175  * 
21176  * @constructor
21177  * Create a new DateField
21178  * @param {Object} config The config object
21179  */
21180
21181 Roo.bootstrap.DateField = function(config){
21182     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21183      this.addEvents({
21184             /**
21185              * @event show
21186              * Fires when this field show.
21187              * @param {Roo.bootstrap.DateField} this
21188              * @param {Mixed} date The date value
21189              */
21190             show : true,
21191             /**
21192              * @event show
21193              * Fires when this field hide.
21194              * @param {Roo.bootstrap.DateField} this
21195              * @param {Mixed} date The date value
21196              */
21197             hide : true,
21198             /**
21199              * @event select
21200              * Fires when select a date.
21201              * @param {Roo.bootstrap.DateField} this
21202              * @param {Mixed} date The date value
21203              */
21204             select : true,
21205             /**
21206              * @event beforeselect
21207              * Fires when before select a date.
21208              * @param {Roo.bootstrap.DateField} this
21209              * @param {Mixed} date The date value
21210              */
21211             beforeselect : true
21212         });
21213 };
21214
21215 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21216     
21217     /**
21218      * @cfg {String} format
21219      * The default date format string which can be overriden for localization support.  The format must be
21220      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21221      */
21222     format : "m/d/y",
21223     /**
21224      * @cfg {String} altFormats
21225      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21226      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21227      */
21228     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21229     
21230     weekStart : 0,
21231     
21232     viewMode : '',
21233     
21234     minViewMode : '',
21235     
21236     todayHighlight : false,
21237     
21238     todayBtn: false,
21239     
21240     language: 'en',
21241     
21242     keyboardNavigation: true,
21243     
21244     calendarWeeks: false,
21245     
21246     startDate: -Infinity,
21247     
21248     endDate: Infinity,
21249     
21250     daysOfWeekDisabled: [],
21251     
21252     _events: [],
21253     
21254     singleMode : false,
21255     
21256     UTCDate: function()
21257     {
21258         return new Date(Date.UTC.apply(Date, arguments));
21259     },
21260     
21261     UTCToday: function()
21262     {
21263         var today = new Date();
21264         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21265     },
21266     
21267     getDate: function() {
21268             var d = this.getUTCDate();
21269             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21270     },
21271     
21272     getUTCDate: function() {
21273             return this.date;
21274     },
21275     
21276     setDate: function(d) {
21277             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21278     },
21279     
21280     setUTCDate: function(d) {
21281             this.date = d;
21282             this.setValue(this.formatDate(this.date));
21283     },
21284         
21285     onRender: function(ct, position)
21286     {
21287         
21288         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21289         
21290         this.language = this.language || 'en';
21291         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21292         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21293         
21294         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21295         this.format = this.format || 'm/d/y';
21296         this.isInline = false;
21297         this.isInput = true;
21298         this.component = this.el.select('.add-on', true).first() || false;
21299         this.component = (this.component && this.component.length === 0) ? false : this.component;
21300         this.hasInput = this.component && this.inputEl().length;
21301         
21302         if (typeof(this.minViewMode === 'string')) {
21303             switch (this.minViewMode) {
21304                 case 'months':
21305                     this.minViewMode = 1;
21306                     break;
21307                 case 'years':
21308                     this.minViewMode = 2;
21309                     break;
21310                 default:
21311                     this.minViewMode = 0;
21312                     break;
21313             }
21314         }
21315         
21316         if (typeof(this.viewMode === 'string')) {
21317             switch (this.viewMode) {
21318                 case 'months':
21319                     this.viewMode = 1;
21320                     break;
21321                 case 'years':
21322                     this.viewMode = 2;
21323                     break;
21324                 default:
21325                     this.viewMode = 0;
21326                     break;
21327             }
21328         }
21329                 
21330         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21331         
21332 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21333         
21334         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21335         
21336         this.picker().on('mousedown', this.onMousedown, this);
21337         this.picker().on('click', this.onClick, this);
21338         
21339         this.picker().addClass('datepicker-dropdown');
21340         
21341         this.startViewMode = this.viewMode;
21342         
21343         if(this.singleMode){
21344             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21345                 v.setVisibilityMode(Roo.Element.DISPLAY);
21346                 v.hide();
21347             });
21348             
21349             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21350                 v.setStyle('width', '189px');
21351             });
21352         }
21353         
21354         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21355             if(!this.calendarWeeks){
21356                 v.remove();
21357                 return;
21358             }
21359             
21360             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21361             v.attr('colspan', function(i, val){
21362                 return parseInt(val) + 1;
21363             });
21364         });
21365                         
21366         
21367         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21368         
21369         this.setStartDate(this.startDate);
21370         this.setEndDate(this.endDate);
21371         
21372         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21373         
21374         this.fillDow();
21375         this.fillMonths();
21376         this.update();
21377         this.showMode();
21378         
21379         if(this.isInline) {
21380             this.showPopup();
21381         }
21382     },
21383     
21384     picker : function()
21385     {
21386         return this.pickerEl;
21387 //        return this.el.select('.datepicker', true).first();
21388     },
21389     
21390     fillDow: function()
21391     {
21392         var dowCnt = this.weekStart;
21393         
21394         var dow = {
21395             tag: 'tr',
21396             cn: [
21397                 
21398             ]
21399         };
21400         
21401         if(this.calendarWeeks){
21402             dow.cn.push({
21403                 tag: 'th',
21404                 cls: 'cw',
21405                 html: '&nbsp;'
21406             })
21407         }
21408         
21409         while (dowCnt < this.weekStart + 7) {
21410             dow.cn.push({
21411                 tag: 'th',
21412                 cls: 'dow',
21413                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21414             });
21415         }
21416         
21417         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21418     },
21419     
21420     fillMonths: function()
21421     {    
21422         var i = 0;
21423         var months = this.picker().select('>.datepicker-months td', true).first();
21424         
21425         months.dom.innerHTML = '';
21426         
21427         while (i < 12) {
21428             var month = {
21429                 tag: 'span',
21430                 cls: 'month',
21431                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21432             };
21433             
21434             months.createChild(month);
21435         }
21436         
21437     },
21438     
21439     update: function()
21440     {
21441         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;
21442         
21443         if (this.date < this.startDate) {
21444             this.viewDate = new Date(this.startDate);
21445         } else if (this.date > this.endDate) {
21446             this.viewDate = new Date(this.endDate);
21447         } else {
21448             this.viewDate = new Date(this.date);
21449         }
21450         
21451         this.fill();
21452     },
21453     
21454     fill: function() 
21455     {
21456         var d = new Date(this.viewDate),
21457                 year = d.getUTCFullYear(),
21458                 month = d.getUTCMonth(),
21459                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21460                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21461                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21462                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21463                 currentDate = this.date && this.date.valueOf(),
21464                 today = this.UTCToday();
21465         
21466         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21467         
21468 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21469         
21470 //        this.picker.select('>tfoot th.today').
21471 //                                              .text(dates[this.language].today)
21472 //                                              .toggle(this.todayBtn !== false);
21473     
21474         this.updateNavArrows();
21475         this.fillMonths();
21476                                                 
21477         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21478         
21479         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21480          
21481         prevMonth.setUTCDate(day);
21482         
21483         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21484         
21485         var nextMonth = new Date(prevMonth);
21486         
21487         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21488         
21489         nextMonth = nextMonth.valueOf();
21490         
21491         var fillMonths = false;
21492         
21493         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21494         
21495         while(prevMonth.valueOf() <= nextMonth) {
21496             var clsName = '';
21497             
21498             if (prevMonth.getUTCDay() === this.weekStart) {
21499                 if(fillMonths){
21500                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21501                 }
21502                     
21503                 fillMonths = {
21504                     tag: 'tr',
21505                     cn: []
21506                 };
21507                 
21508                 if(this.calendarWeeks){
21509                     // ISO 8601: First week contains first thursday.
21510                     // ISO also states week starts on Monday, but we can be more abstract here.
21511                     var
21512                     // Start of current week: based on weekstart/current date
21513                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21514                     // Thursday of this week
21515                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21516                     // First Thursday of year, year from thursday
21517                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21518                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21519                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21520                     
21521                     fillMonths.cn.push({
21522                         tag: 'td',
21523                         cls: 'cw',
21524                         html: calWeek
21525                     });
21526                 }
21527             }
21528             
21529             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21530                 clsName += ' old';
21531             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21532                 clsName += ' new';
21533             }
21534             if (this.todayHighlight &&
21535                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21536                 prevMonth.getUTCMonth() == today.getMonth() &&
21537                 prevMonth.getUTCDate() == today.getDate()) {
21538                 clsName += ' today';
21539             }
21540             
21541             if (currentDate && prevMonth.valueOf() === currentDate) {
21542                 clsName += ' active';
21543             }
21544             
21545             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21546                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21547                     clsName += ' disabled';
21548             }
21549             
21550             fillMonths.cn.push({
21551                 tag: 'td',
21552                 cls: 'day ' + clsName,
21553                 html: prevMonth.getDate()
21554             });
21555             
21556             prevMonth.setDate(prevMonth.getDate()+1);
21557         }
21558           
21559         var currentYear = this.date && this.date.getUTCFullYear();
21560         var currentMonth = this.date && this.date.getUTCMonth();
21561         
21562         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21563         
21564         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21565             v.removeClass('active');
21566             
21567             if(currentYear === year && k === currentMonth){
21568                 v.addClass('active');
21569             }
21570             
21571             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21572                 v.addClass('disabled');
21573             }
21574             
21575         });
21576         
21577         
21578         year = parseInt(year/10, 10) * 10;
21579         
21580         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21581         
21582         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21583         
21584         year -= 1;
21585         for (var i = -1; i < 11; i++) {
21586             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21587                 tag: 'span',
21588                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21589                 html: year
21590             });
21591             
21592             year += 1;
21593         }
21594     },
21595     
21596     showMode: function(dir) 
21597     {
21598         if (dir) {
21599             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21600         }
21601         
21602         Roo.each(this.picker().select('>div',true).elements, function(v){
21603             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21604             v.hide();
21605         });
21606         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21607     },
21608     
21609     place: function()
21610     {
21611         if(this.isInline) {
21612             return;
21613         }
21614         
21615         this.picker().removeClass(['bottom', 'top']);
21616         
21617         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21618             /*
21619              * place to the top of element!
21620              *
21621              */
21622             
21623             this.picker().addClass('top');
21624             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21625             
21626             return;
21627         }
21628         
21629         this.picker().addClass('bottom');
21630         
21631         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21632     },
21633     
21634     parseDate : function(value)
21635     {
21636         if(!value || value instanceof Date){
21637             return value;
21638         }
21639         var v = Date.parseDate(value, this.format);
21640         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21641             v = Date.parseDate(value, 'Y-m-d');
21642         }
21643         if(!v && this.altFormats){
21644             if(!this.altFormatsArray){
21645                 this.altFormatsArray = this.altFormats.split("|");
21646             }
21647             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21648                 v = Date.parseDate(value, this.altFormatsArray[i]);
21649             }
21650         }
21651         return v;
21652     },
21653     
21654     formatDate : function(date, fmt)
21655     {   
21656         return (!date || !(date instanceof Date)) ?
21657         date : date.dateFormat(fmt || this.format);
21658     },
21659     
21660     onFocus : function()
21661     {
21662         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21663         this.showPopup();
21664     },
21665     
21666     onBlur : function()
21667     {
21668         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21669         
21670         var d = this.inputEl().getValue();
21671         
21672         this.setValue(d);
21673                 
21674         this.hidePopup();
21675     },
21676     
21677     showPopup : function()
21678     {
21679         this.picker().show();
21680         this.update();
21681         this.place();
21682         
21683         this.fireEvent('showpopup', this, this.date);
21684     },
21685     
21686     hidePopup : function()
21687     {
21688         if(this.isInline) {
21689             return;
21690         }
21691         this.picker().hide();
21692         this.viewMode = this.startViewMode;
21693         this.showMode();
21694         
21695         this.fireEvent('hidepopup', this, this.date);
21696         
21697     },
21698     
21699     onMousedown: function(e)
21700     {
21701         e.stopPropagation();
21702         e.preventDefault();
21703     },
21704     
21705     keyup: function(e)
21706     {
21707         Roo.bootstrap.DateField.superclass.keyup.call(this);
21708         this.update();
21709     },
21710
21711     setValue: function(v)
21712     {
21713         if(this.fireEvent('beforeselect', this, v) !== false){
21714             var d = new Date(this.parseDate(v) ).clearTime();
21715         
21716             if(isNaN(d.getTime())){
21717                 this.date = this.viewDate = '';
21718                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21719                 return;
21720             }
21721
21722             v = this.formatDate(d);
21723
21724             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21725
21726             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21727
21728             this.update();
21729
21730             this.fireEvent('select', this, this.date);
21731         }
21732     },
21733     
21734     getValue: function()
21735     {
21736         return this.formatDate(this.date);
21737     },
21738     
21739     fireKey: function(e)
21740     {
21741         if (!this.picker().isVisible()){
21742             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21743                 this.showPopup();
21744             }
21745             return;
21746         }
21747         
21748         var dateChanged = false,
21749         dir, day, month,
21750         newDate, newViewDate;
21751         
21752         switch(e.keyCode){
21753             case 27: // escape
21754                 this.hidePopup();
21755                 e.preventDefault();
21756                 break;
21757             case 37: // left
21758             case 39: // right
21759                 if (!this.keyboardNavigation) {
21760                     break;
21761                 }
21762                 dir = e.keyCode == 37 ? -1 : 1;
21763                 
21764                 if (e.ctrlKey){
21765                     newDate = this.moveYear(this.date, dir);
21766                     newViewDate = this.moveYear(this.viewDate, dir);
21767                 } else if (e.shiftKey){
21768                     newDate = this.moveMonth(this.date, dir);
21769                     newViewDate = this.moveMonth(this.viewDate, dir);
21770                 } else {
21771                     newDate = new Date(this.date);
21772                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21773                     newViewDate = new Date(this.viewDate);
21774                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21775                 }
21776                 if (this.dateWithinRange(newDate)){
21777                     this.date = newDate;
21778                     this.viewDate = newViewDate;
21779                     this.setValue(this.formatDate(this.date));
21780 //                    this.update();
21781                     e.preventDefault();
21782                     dateChanged = true;
21783                 }
21784                 break;
21785             case 38: // up
21786             case 40: // down
21787                 if (!this.keyboardNavigation) {
21788                     break;
21789                 }
21790                 dir = e.keyCode == 38 ? -1 : 1;
21791                 if (e.ctrlKey){
21792                     newDate = this.moveYear(this.date, dir);
21793                     newViewDate = this.moveYear(this.viewDate, dir);
21794                 } else if (e.shiftKey){
21795                     newDate = this.moveMonth(this.date, dir);
21796                     newViewDate = this.moveMonth(this.viewDate, dir);
21797                 } else {
21798                     newDate = new Date(this.date);
21799                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21800                     newViewDate = new Date(this.viewDate);
21801                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21802                 }
21803                 if (this.dateWithinRange(newDate)){
21804                     this.date = newDate;
21805                     this.viewDate = newViewDate;
21806                     this.setValue(this.formatDate(this.date));
21807 //                    this.update();
21808                     e.preventDefault();
21809                     dateChanged = true;
21810                 }
21811                 break;
21812             case 13: // enter
21813                 this.setValue(this.formatDate(this.date));
21814                 this.hidePopup();
21815                 e.preventDefault();
21816                 break;
21817             case 9: // tab
21818                 this.setValue(this.formatDate(this.date));
21819                 this.hidePopup();
21820                 break;
21821             case 16: // shift
21822             case 17: // ctrl
21823             case 18: // alt
21824                 break;
21825             default :
21826                 this.hidePopup();
21827                 
21828         }
21829     },
21830     
21831     
21832     onClick: function(e) 
21833     {
21834         e.stopPropagation();
21835         e.preventDefault();
21836         
21837         var target = e.getTarget();
21838         
21839         if(target.nodeName.toLowerCase() === 'i'){
21840             target = Roo.get(target).dom.parentNode;
21841         }
21842         
21843         var nodeName = target.nodeName;
21844         var className = target.className;
21845         var html = target.innerHTML;
21846         //Roo.log(nodeName);
21847         
21848         switch(nodeName.toLowerCase()) {
21849             case 'th':
21850                 switch(className) {
21851                     case 'switch':
21852                         this.showMode(1);
21853                         break;
21854                     case 'prev':
21855                     case 'next':
21856                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21857                         switch(this.viewMode){
21858                                 case 0:
21859                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21860                                         break;
21861                                 case 1:
21862                                 case 2:
21863                                         this.viewDate = this.moveYear(this.viewDate, dir);
21864                                         break;
21865                         }
21866                         this.fill();
21867                         break;
21868                     case 'today':
21869                         var date = new Date();
21870                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21871 //                        this.fill()
21872                         this.setValue(this.formatDate(this.date));
21873                         
21874                         this.hidePopup();
21875                         break;
21876                 }
21877                 break;
21878             case 'span':
21879                 if (className.indexOf('disabled') < 0) {
21880                     this.viewDate.setUTCDate(1);
21881                     if (className.indexOf('month') > -1) {
21882                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21883                     } else {
21884                         var year = parseInt(html, 10) || 0;
21885                         this.viewDate.setUTCFullYear(year);
21886                         
21887                     }
21888                     
21889                     if(this.singleMode){
21890                         this.setValue(this.formatDate(this.viewDate));
21891                         this.hidePopup();
21892                         return;
21893                     }
21894                     
21895                     this.showMode(-1);
21896                     this.fill();
21897                 }
21898                 break;
21899                 
21900             case 'td':
21901                 //Roo.log(className);
21902                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21903                     var day = parseInt(html, 10) || 1;
21904                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21905                         month = (this.viewDate || new Date()).getUTCMonth();
21906
21907                     if (className.indexOf('old') > -1) {
21908                         if(month === 0 ){
21909                             month = 11;
21910                             year -= 1;
21911                         }else{
21912                             month -= 1;
21913                         }
21914                     } else if (className.indexOf('new') > -1) {
21915                         if (month == 11) {
21916                             month = 0;
21917                             year += 1;
21918                         } else {
21919                             month += 1;
21920                         }
21921                     }
21922                     //Roo.log([year,month,day]);
21923                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21924                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21925 //                    this.fill();
21926                     //Roo.log(this.formatDate(this.date));
21927                     this.setValue(this.formatDate(this.date));
21928                     this.hidePopup();
21929                 }
21930                 break;
21931         }
21932     },
21933     
21934     setStartDate: function(startDate)
21935     {
21936         this.startDate = startDate || -Infinity;
21937         if (this.startDate !== -Infinity) {
21938             this.startDate = this.parseDate(this.startDate);
21939         }
21940         this.update();
21941         this.updateNavArrows();
21942     },
21943
21944     setEndDate: function(endDate)
21945     {
21946         this.endDate = endDate || Infinity;
21947         if (this.endDate !== Infinity) {
21948             this.endDate = this.parseDate(this.endDate);
21949         }
21950         this.update();
21951         this.updateNavArrows();
21952     },
21953     
21954     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21955     {
21956         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21957         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21958             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21959         }
21960         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21961             return parseInt(d, 10);
21962         });
21963         this.update();
21964         this.updateNavArrows();
21965     },
21966     
21967     updateNavArrows: function() 
21968     {
21969         if(this.singleMode){
21970             return;
21971         }
21972         
21973         var d = new Date(this.viewDate),
21974         year = d.getUTCFullYear(),
21975         month = d.getUTCMonth();
21976         
21977         Roo.each(this.picker().select('.prev', true).elements, function(v){
21978             v.show();
21979             switch (this.viewMode) {
21980                 case 0:
21981
21982                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21983                         v.hide();
21984                     }
21985                     break;
21986                 case 1:
21987                 case 2:
21988                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21989                         v.hide();
21990                     }
21991                     break;
21992             }
21993         });
21994         
21995         Roo.each(this.picker().select('.next', true).elements, function(v){
21996             v.show();
21997             switch (this.viewMode) {
21998                 case 0:
21999
22000                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22001                         v.hide();
22002                     }
22003                     break;
22004                 case 1:
22005                 case 2:
22006                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22007                         v.hide();
22008                     }
22009                     break;
22010             }
22011         })
22012     },
22013     
22014     moveMonth: function(date, dir)
22015     {
22016         if (!dir) {
22017             return date;
22018         }
22019         var new_date = new Date(date.valueOf()),
22020         day = new_date.getUTCDate(),
22021         month = new_date.getUTCMonth(),
22022         mag = Math.abs(dir),
22023         new_month, test;
22024         dir = dir > 0 ? 1 : -1;
22025         if (mag == 1){
22026             test = dir == -1
22027             // If going back one month, make sure month is not current month
22028             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22029             ? function(){
22030                 return new_date.getUTCMonth() == month;
22031             }
22032             // If going forward one month, make sure month is as expected
22033             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22034             : function(){
22035                 return new_date.getUTCMonth() != new_month;
22036             };
22037             new_month = month + dir;
22038             new_date.setUTCMonth(new_month);
22039             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22040             if (new_month < 0 || new_month > 11) {
22041                 new_month = (new_month + 12) % 12;
22042             }
22043         } else {
22044             // For magnitudes >1, move one month at a time...
22045             for (var i=0; i<mag; i++) {
22046                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22047                 new_date = this.moveMonth(new_date, dir);
22048             }
22049             // ...then reset the day, keeping it in the new month
22050             new_month = new_date.getUTCMonth();
22051             new_date.setUTCDate(day);
22052             test = function(){
22053                 return new_month != new_date.getUTCMonth();
22054             };
22055         }
22056         // Common date-resetting loop -- if date is beyond end of month, make it
22057         // end of month
22058         while (test()){
22059             new_date.setUTCDate(--day);
22060             new_date.setUTCMonth(new_month);
22061         }
22062         return new_date;
22063     },
22064
22065     moveYear: function(date, dir)
22066     {
22067         return this.moveMonth(date, dir*12);
22068     },
22069
22070     dateWithinRange: function(date)
22071     {
22072         return date >= this.startDate && date <= this.endDate;
22073     },
22074
22075     
22076     remove: function() 
22077     {
22078         this.picker().remove();
22079     },
22080     
22081     validateValue : function(value)
22082     {
22083         if(this.getVisibilityEl().hasClass('hidden')){
22084             return true;
22085         }
22086         
22087         if(value.length < 1)  {
22088             if(this.allowBlank){
22089                 return true;
22090             }
22091             return false;
22092         }
22093         
22094         if(value.length < this.minLength){
22095             return false;
22096         }
22097         if(value.length > this.maxLength){
22098             return false;
22099         }
22100         if(this.vtype){
22101             var vt = Roo.form.VTypes;
22102             if(!vt[this.vtype](value, this)){
22103                 return false;
22104             }
22105         }
22106         if(typeof this.validator == "function"){
22107             var msg = this.validator(value);
22108             if(msg !== true){
22109                 return false;
22110             }
22111         }
22112         
22113         if(this.regex && !this.regex.test(value)){
22114             return false;
22115         }
22116         
22117         if(typeof(this.parseDate(value)) == 'undefined'){
22118             return false;
22119         }
22120         
22121         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22122             return false;
22123         }      
22124         
22125         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22126             return false;
22127         } 
22128         
22129         
22130         return true;
22131     },
22132     
22133     reset : function()
22134     {
22135         this.date = this.viewDate = '';
22136         
22137         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22138     }
22139    
22140 });
22141
22142 Roo.apply(Roo.bootstrap.DateField,  {
22143     
22144     head : {
22145         tag: 'thead',
22146         cn: [
22147         {
22148             tag: 'tr',
22149             cn: [
22150             {
22151                 tag: 'th',
22152                 cls: 'prev',
22153                 html: '<i class="fa fa-arrow-left"/>'
22154             },
22155             {
22156                 tag: 'th',
22157                 cls: 'switch',
22158                 colspan: '5'
22159             },
22160             {
22161                 tag: 'th',
22162                 cls: 'next',
22163                 html: '<i class="fa fa-arrow-right"/>'
22164             }
22165
22166             ]
22167         }
22168         ]
22169     },
22170     
22171     content : {
22172         tag: 'tbody',
22173         cn: [
22174         {
22175             tag: 'tr',
22176             cn: [
22177             {
22178                 tag: 'td',
22179                 colspan: '7'
22180             }
22181             ]
22182         }
22183         ]
22184     },
22185     
22186     footer : {
22187         tag: 'tfoot',
22188         cn: [
22189         {
22190             tag: 'tr',
22191             cn: [
22192             {
22193                 tag: 'th',
22194                 colspan: '7',
22195                 cls: 'today'
22196             }
22197                     
22198             ]
22199         }
22200         ]
22201     },
22202     
22203     dates:{
22204         en: {
22205             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22206             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22207             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22208             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22209             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22210             today: "Today"
22211         }
22212     },
22213     
22214     modes: [
22215     {
22216         clsName: 'days',
22217         navFnc: 'Month',
22218         navStep: 1
22219     },
22220     {
22221         clsName: 'months',
22222         navFnc: 'FullYear',
22223         navStep: 1
22224     },
22225     {
22226         clsName: 'years',
22227         navFnc: 'FullYear',
22228         navStep: 10
22229     }]
22230 });
22231
22232 Roo.apply(Roo.bootstrap.DateField,  {
22233   
22234     template : {
22235         tag: 'div',
22236         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22237         cn: [
22238         {
22239             tag: 'div',
22240             cls: 'datepicker-days',
22241             cn: [
22242             {
22243                 tag: 'table',
22244                 cls: 'table-condensed',
22245                 cn:[
22246                 Roo.bootstrap.DateField.head,
22247                 {
22248                     tag: 'tbody'
22249                 },
22250                 Roo.bootstrap.DateField.footer
22251                 ]
22252             }
22253             ]
22254         },
22255         {
22256             tag: 'div',
22257             cls: 'datepicker-months',
22258             cn: [
22259             {
22260                 tag: 'table',
22261                 cls: 'table-condensed',
22262                 cn:[
22263                 Roo.bootstrap.DateField.head,
22264                 Roo.bootstrap.DateField.content,
22265                 Roo.bootstrap.DateField.footer
22266                 ]
22267             }
22268             ]
22269         },
22270         {
22271             tag: 'div',
22272             cls: 'datepicker-years',
22273             cn: [
22274             {
22275                 tag: 'table',
22276                 cls: 'table-condensed',
22277                 cn:[
22278                 Roo.bootstrap.DateField.head,
22279                 Roo.bootstrap.DateField.content,
22280                 Roo.bootstrap.DateField.footer
22281                 ]
22282             }
22283             ]
22284         }
22285         ]
22286     }
22287 });
22288
22289  
22290
22291  /*
22292  * - LGPL
22293  *
22294  * TimeField
22295  * 
22296  */
22297
22298 /**
22299  * @class Roo.bootstrap.TimeField
22300  * @extends Roo.bootstrap.Input
22301  * Bootstrap DateField class
22302  * 
22303  * 
22304  * @constructor
22305  * Create a new TimeField
22306  * @param {Object} config The config object
22307  */
22308
22309 Roo.bootstrap.TimeField = function(config){
22310     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22311     this.addEvents({
22312             /**
22313              * @event show
22314              * Fires when this field show.
22315              * @param {Roo.bootstrap.DateField} thisthis
22316              * @param {Mixed} date The date value
22317              */
22318             show : true,
22319             /**
22320              * @event show
22321              * Fires when this field hide.
22322              * @param {Roo.bootstrap.DateField} this
22323              * @param {Mixed} date The date value
22324              */
22325             hide : true,
22326             /**
22327              * @event select
22328              * Fires when select a date.
22329              * @param {Roo.bootstrap.DateField} this
22330              * @param {Mixed} date The date value
22331              */
22332             select : true
22333         });
22334 };
22335
22336 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22337     
22338     /**
22339      * @cfg {String} format
22340      * The default time format string which can be overriden for localization support.  The format must be
22341      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22342      */
22343     format : "H:i",
22344
22345     getAutoCreate : function()
22346     {
22347         this.after = '<i class="fa far fa-clock"></i>';
22348         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22349         
22350          
22351     },
22352     onRender: function(ct, position)
22353     {
22354         
22355         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22356                 
22357         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22358         
22359         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22360         
22361         this.pop = this.picker().select('>.datepicker-time',true).first();
22362         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22363         
22364         this.picker().on('mousedown', this.onMousedown, this);
22365         this.picker().on('click', this.onClick, this);
22366         
22367         this.picker().addClass('datepicker-dropdown');
22368     
22369         this.fillTime();
22370         this.update();
22371             
22372         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22373         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22374         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22375         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22376         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22377         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22378
22379     },
22380     
22381     fireKey: function(e){
22382         if (!this.picker().isVisible()){
22383             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22384                 this.show();
22385             }
22386             return;
22387         }
22388
22389         e.preventDefault();
22390         
22391         switch(e.keyCode){
22392             case 27: // escape
22393                 this.hide();
22394                 break;
22395             case 37: // left
22396             case 39: // right
22397                 this.onTogglePeriod();
22398                 break;
22399             case 38: // up
22400                 this.onIncrementMinutes();
22401                 break;
22402             case 40: // down
22403                 this.onDecrementMinutes();
22404                 break;
22405             case 13: // enter
22406             case 9: // tab
22407                 this.setTime();
22408                 break;
22409         }
22410     },
22411     
22412     onClick: function(e) {
22413         e.stopPropagation();
22414         e.preventDefault();
22415     },
22416     
22417     picker : function()
22418     {
22419         return this.pickerEl;
22420     },
22421     
22422     fillTime: function()
22423     {    
22424         var time = this.pop.select('tbody', true).first();
22425         
22426         time.dom.innerHTML = '';
22427         
22428         time.createChild({
22429             tag: 'tr',
22430             cn: [
22431                 {
22432                     tag: 'td',
22433                     cn: [
22434                         {
22435                             tag: 'a',
22436                             href: '#',
22437                             cls: 'btn',
22438                             cn: [
22439                                 {
22440                                     tag: 'i',
22441                                     cls: 'hours-up fa fas fa-chevron-up'
22442                                 }
22443                             ]
22444                         } 
22445                     ]
22446                 },
22447                 {
22448                     tag: 'td',
22449                     cls: 'separator'
22450                 },
22451                 {
22452                     tag: 'td',
22453                     cn: [
22454                         {
22455                             tag: 'a',
22456                             href: '#',
22457                             cls: 'btn',
22458                             cn: [
22459                                 {
22460                                     tag: 'i',
22461                                     cls: 'minutes-up fa fas fa-chevron-up'
22462                                 }
22463                             ]
22464                         }
22465                     ]
22466                 },
22467                 {
22468                     tag: 'td',
22469                     cls: 'separator'
22470                 }
22471             ]
22472         });
22473         
22474         time.createChild({
22475             tag: 'tr',
22476             cn: [
22477                 {
22478                     tag: 'td',
22479                     cn: [
22480                         {
22481                             tag: 'span',
22482                             cls: 'timepicker-hour',
22483                             html: '00'
22484                         }  
22485                     ]
22486                 },
22487                 {
22488                     tag: 'td',
22489                     cls: 'separator',
22490                     html: ':'
22491                 },
22492                 {
22493                     tag: 'td',
22494                     cn: [
22495                         {
22496                             tag: 'span',
22497                             cls: 'timepicker-minute',
22498                             html: '00'
22499                         }  
22500                     ]
22501                 },
22502                 {
22503                     tag: 'td',
22504                     cls: 'separator'
22505                 },
22506                 {
22507                     tag: 'td',
22508                     cn: [
22509                         {
22510                             tag: 'button',
22511                             type: 'button',
22512                             cls: 'btn btn-primary period',
22513                             html: 'AM'
22514                             
22515                         }
22516                     ]
22517                 }
22518             ]
22519         });
22520         
22521         time.createChild({
22522             tag: 'tr',
22523             cn: [
22524                 {
22525                     tag: 'td',
22526                     cn: [
22527                         {
22528                             tag: 'a',
22529                             href: '#',
22530                             cls: 'btn',
22531                             cn: [
22532                                 {
22533                                     tag: 'span',
22534                                     cls: 'hours-down fa fas fa-chevron-down'
22535                                 }
22536                             ]
22537                         }
22538                     ]
22539                 },
22540                 {
22541                     tag: 'td',
22542                     cls: 'separator'
22543                 },
22544                 {
22545                     tag: 'td',
22546                     cn: [
22547                         {
22548                             tag: 'a',
22549                             href: '#',
22550                             cls: 'btn',
22551                             cn: [
22552                                 {
22553                                     tag: 'span',
22554                                     cls: 'minutes-down fa fas fa-chevron-down'
22555                                 }
22556                             ]
22557                         }
22558                     ]
22559                 },
22560                 {
22561                     tag: 'td',
22562                     cls: 'separator'
22563                 }
22564             ]
22565         });
22566         
22567     },
22568     
22569     update: function()
22570     {
22571         
22572         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22573         
22574         this.fill();
22575     },
22576     
22577     fill: function() 
22578     {
22579         var hours = this.time.getHours();
22580         var minutes = this.time.getMinutes();
22581         var period = 'AM';
22582         
22583         if(hours > 11){
22584             period = 'PM';
22585         }
22586         
22587         if(hours == 0){
22588             hours = 12;
22589         }
22590         
22591         
22592         if(hours > 12){
22593             hours = hours - 12;
22594         }
22595         
22596         if(hours < 10){
22597             hours = '0' + hours;
22598         }
22599         
22600         if(minutes < 10){
22601             minutes = '0' + minutes;
22602         }
22603         
22604         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22605         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22606         this.pop.select('button', true).first().dom.innerHTML = period;
22607         
22608     },
22609     
22610     place: function()
22611     {   
22612         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22613         
22614         var cls = ['bottom'];
22615         
22616         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22617             cls.pop();
22618             cls.push('top');
22619         }
22620         
22621         cls.push('right');
22622         
22623         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22624             cls.pop();
22625             cls.push('left');
22626         }
22627         //this.picker().setXY(20000,20000);
22628         this.picker().addClass(cls.join('-'));
22629         
22630         var _this = this;
22631         
22632         Roo.each(cls, function(c){
22633             if(c == 'bottom'){
22634                 (function() {
22635                  //  
22636                 }).defer(200);
22637                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22638                 //_this.picker().setTop(_this.inputEl().getHeight());
22639                 return;
22640             }
22641             if(c == 'top'){
22642                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22643                 
22644                 //_this.picker().setTop(0 - _this.picker().getHeight());
22645                 return;
22646             }
22647             /*
22648             if(c == 'left'){
22649                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22650                 return;
22651             }
22652             if(c == 'right'){
22653                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22654                 return;
22655             }
22656             */
22657         });
22658         
22659     },
22660   
22661     onFocus : function()
22662     {
22663         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22664         this.show();
22665     },
22666     
22667     onBlur : function()
22668     {
22669         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22670         this.hide();
22671     },
22672     
22673     show : function()
22674     {
22675         this.picker().show();
22676         this.pop.show();
22677         this.update();
22678         this.place();
22679         
22680         this.fireEvent('show', this, this.date);
22681     },
22682     
22683     hide : function()
22684     {
22685         this.picker().hide();
22686         this.pop.hide();
22687         
22688         this.fireEvent('hide', this, this.date);
22689     },
22690     
22691     setTime : function()
22692     {
22693         this.hide();
22694         this.setValue(this.time.format(this.format));
22695         
22696         this.fireEvent('select', this, this.date);
22697         
22698         
22699     },
22700     
22701     onMousedown: function(e){
22702         e.stopPropagation();
22703         e.preventDefault();
22704     },
22705     
22706     onIncrementHours: function()
22707     {
22708         Roo.log('onIncrementHours');
22709         this.time = this.time.add(Date.HOUR, 1);
22710         this.update();
22711         
22712     },
22713     
22714     onDecrementHours: function()
22715     {
22716         Roo.log('onDecrementHours');
22717         this.time = this.time.add(Date.HOUR, -1);
22718         this.update();
22719     },
22720     
22721     onIncrementMinutes: function()
22722     {
22723         Roo.log('onIncrementMinutes');
22724         this.time = this.time.add(Date.MINUTE, 1);
22725         this.update();
22726     },
22727     
22728     onDecrementMinutes: function()
22729     {
22730         Roo.log('onDecrementMinutes');
22731         this.time = this.time.add(Date.MINUTE, -1);
22732         this.update();
22733     },
22734     
22735     onTogglePeriod: function()
22736     {
22737         Roo.log('onTogglePeriod');
22738         this.time = this.time.add(Date.HOUR, 12);
22739         this.update();
22740     }
22741     
22742    
22743 });
22744  
22745
22746 Roo.apply(Roo.bootstrap.TimeField,  {
22747   
22748     template : {
22749         tag: 'div',
22750         cls: 'datepicker dropdown-menu',
22751         cn: [
22752             {
22753                 tag: 'div',
22754                 cls: 'datepicker-time',
22755                 cn: [
22756                 {
22757                     tag: 'table',
22758                     cls: 'table-condensed',
22759                     cn:[
22760                         {
22761                             tag: 'tbody',
22762                             cn: [
22763                                 {
22764                                     tag: 'tr',
22765                                     cn: [
22766                                     {
22767                                         tag: 'td',
22768                                         colspan: '7'
22769                                     }
22770                                     ]
22771                                 }
22772                             ]
22773                         },
22774                         {
22775                             tag: 'tfoot',
22776                             cn: [
22777                                 {
22778                                     tag: 'tr',
22779                                     cn: [
22780                                     {
22781                                         tag: 'th',
22782                                         colspan: '7',
22783                                         cls: '',
22784                                         cn: [
22785                                             {
22786                                                 tag: 'button',
22787                                                 cls: 'btn btn-info ok',
22788                                                 html: 'OK'
22789                                             }
22790                                         ]
22791                                     }
22792                     
22793                                     ]
22794                                 }
22795                             ]
22796                         }
22797                     ]
22798                 }
22799                 ]
22800             }
22801         ]
22802     }
22803 });
22804
22805  
22806
22807  /*
22808  * - LGPL
22809  *
22810  * MonthField
22811  * 
22812  */
22813
22814 /**
22815  * @class Roo.bootstrap.MonthField
22816  * @extends Roo.bootstrap.Input
22817  * Bootstrap MonthField class
22818  * 
22819  * @cfg {String} language default en
22820  * 
22821  * @constructor
22822  * Create a new MonthField
22823  * @param {Object} config The config object
22824  */
22825
22826 Roo.bootstrap.MonthField = function(config){
22827     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22828     
22829     this.addEvents({
22830         /**
22831          * @event show
22832          * Fires when this field show.
22833          * @param {Roo.bootstrap.MonthField} this
22834          * @param {Mixed} date The date value
22835          */
22836         show : true,
22837         /**
22838          * @event show
22839          * Fires when this field hide.
22840          * @param {Roo.bootstrap.MonthField} this
22841          * @param {Mixed} date The date value
22842          */
22843         hide : true,
22844         /**
22845          * @event select
22846          * Fires when select a date.
22847          * @param {Roo.bootstrap.MonthField} this
22848          * @param {String} oldvalue The old value
22849          * @param {String} newvalue The new value
22850          */
22851         select : true
22852     });
22853 };
22854
22855 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22856     
22857     onRender: function(ct, position)
22858     {
22859         
22860         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22861         
22862         this.language = this.language || 'en';
22863         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22864         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22865         
22866         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22867         this.isInline = false;
22868         this.isInput = true;
22869         this.component = this.el.select('.add-on', true).first() || false;
22870         this.component = (this.component && this.component.length === 0) ? false : this.component;
22871         this.hasInput = this.component && this.inputEL().length;
22872         
22873         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22874         
22875         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22876         
22877         this.picker().on('mousedown', this.onMousedown, this);
22878         this.picker().on('click', this.onClick, this);
22879         
22880         this.picker().addClass('datepicker-dropdown');
22881         
22882         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22883             v.setStyle('width', '189px');
22884         });
22885         
22886         this.fillMonths();
22887         
22888         this.update();
22889         
22890         if(this.isInline) {
22891             this.show();
22892         }
22893         
22894     },
22895     
22896     setValue: function(v, suppressEvent)
22897     {   
22898         var o = this.getValue();
22899         
22900         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22901         
22902         this.update();
22903
22904         if(suppressEvent !== true){
22905             this.fireEvent('select', this, o, v);
22906         }
22907         
22908     },
22909     
22910     getValue: function()
22911     {
22912         return this.value;
22913     },
22914     
22915     onClick: function(e) 
22916     {
22917         e.stopPropagation();
22918         e.preventDefault();
22919         
22920         var target = e.getTarget();
22921         
22922         if(target.nodeName.toLowerCase() === 'i'){
22923             target = Roo.get(target).dom.parentNode;
22924         }
22925         
22926         var nodeName = target.nodeName;
22927         var className = target.className;
22928         var html = target.innerHTML;
22929         
22930         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22931             return;
22932         }
22933         
22934         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22935         
22936         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22937         
22938         this.hide();
22939                         
22940     },
22941     
22942     picker : function()
22943     {
22944         return this.pickerEl;
22945     },
22946     
22947     fillMonths: function()
22948     {    
22949         var i = 0;
22950         var months = this.picker().select('>.datepicker-months td', true).first();
22951         
22952         months.dom.innerHTML = '';
22953         
22954         while (i < 12) {
22955             var month = {
22956                 tag: 'span',
22957                 cls: 'month',
22958                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22959             };
22960             
22961             months.createChild(month);
22962         }
22963         
22964     },
22965     
22966     update: function()
22967     {
22968         var _this = this;
22969         
22970         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22971             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22972         }
22973         
22974         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22975             e.removeClass('active');
22976             
22977             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22978                 e.addClass('active');
22979             }
22980         })
22981     },
22982     
22983     place: function()
22984     {
22985         if(this.isInline) {
22986             return;
22987         }
22988         
22989         this.picker().removeClass(['bottom', 'top']);
22990         
22991         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22992             /*
22993              * place to the top of element!
22994              *
22995              */
22996             
22997             this.picker().addClass('top');
22998             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22999             
23000             return;
23001         }
23002         
23003         this.picker().addClass('bottom');
23004         
23005         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23006     },
23007     
23008     onFocus : function()
23009     {
23010         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23011         this.show();
23012     },
23013     
23014     onBlur : function()
23015     {
23016         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23017         
23018         var d = this.inputEl().getValue();
23019         
23020         this.setValue(d);
23021                 
23022         this.hide();
23023     },
23024     
23025     show : function()
23026     {
23027         this.picker().show();
23028         this.picker().select('>.datepicker-months', true).first().show();
23029         this.update();
23030         this.place();
23031         
23032         this.fireEvent('show', this, this.date);
23033     },
23034     
23035     hide : function()
23036     {
23037         if(this.isInline) {
23038             return;
23039         }
23040         this.picker().hide();
23041         this.fireEvent('hide', this, this.date);
23042         
23043     },
23044     
23045     onMousedown: function(e)
23046     {
23047         e.stopPropagation();
23048         e.preventDefault();
23049     },
23050     
23051     keyup: function(e)
23052     {
23053         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23054         this.update();
23055     },
23056
23057     fireKey: function(e)
23058     {
23059         if (!this.picker().isVisible()){
23060             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23061                 this.show();
23062             }
23063             return;
23064         }
23065         
23066         var dir;
23067         
23068         switch(e.keyCode){
23069             case 27: // escape
23070                 this.hide();
23071                 e.preventDefault();
23072                 break;
23073             case 37: // left
23074             case 39: // right
23075                 dir = e.keyCode == 37 ? -1 : 1;
23076                 
23077                 this.vIndex = this.vIndex + dir;
23078                 
23079                 if(this.vIndex < 0){
23080                     this.vIndex = 0;
23081                 }
23082                 
23083                 if(this.vIndex > 11){
23084                     this.vIndex = 11;
23085                 }
23086                 
23087                 if(isNaN(this.vIndex)){
23088                     this.vIndex = 0;
23089                 }
23090                 
23091                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23092                 
23093                 break;
23094             case 38: // up
23095             case 40: // down
23096                 
23097                 dir = e.keyCode == 38 ? -1 : 1;
23098                 
23099                 this.vIndex = this.vIndex + dir * 4;
23100                 
23101                 if(this.vIndex < 0){
23102                     this.vIndex = 0;
23103                 }
23104                 
23105                 if(this.vIndex > 11){
23106                     this.vIndex = 11;
23107                 }
23108                 
23109                 if(isNaN(this.vIndex)){
23110                     this.vIndex = 0;
23111                 }
23112                 
23113                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23114                 break;
23115                 
23116             case 13: // enter
23117                 
23118                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23119                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23120                 }
23121                 
23122                 this.hide();
23123                 e.preventDefault();
23124                 break;
23125             case 9: // tab
23126                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23127                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23128                 }
23129                 this.hide();
23130                 break;
23131             case 16: // shift
23132             case 17: // ctrl
23133             case 18: // alt
23134                 break;
23135             default :
23136                 this.hide();
23137                 
23138         }
23139     },
23140     
23141     remove: function() 
23142     {
23143         this.picker().remove();
23144     }
23145    
23146 });
23147
23148 Roo.apply(Roo.bootstrap.MonthField,  {
23149     
23150     content : {
23151         tag: 'tbody',
23152         cn: [
23153         {
23154             tag: 'tr',
23155             cn: [
23156             {
23157                 tag: 'td',
23158                 colspan: '7'
23159             }
23160             ]
23161         }
23162         ]
23163     },
23164     
23165     dates:{
23166         en: {
23167             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23168             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23169         }
23170     }
23171 });
23172
23173 Roo.apply(Roo.bootstrap.MonthField,  {
23174   
23175     template : {
23176         tag: 'div',
23177         cls: 'datepicker dropdown-menu roo-dynamic',
23178         cn: [
23179             {
23180                 tag: 'div',
23181                 cls: 'datepicker-months',
23182                 cn: [
23183                 {
23184                     tag: 'table',
23185                     cls: 'table-condensed',
23186                     cn:[
23187                         Roo.bootstrap.DateField.content
23188                     ]
23189                 }
23190                 ]
23191             }
23192         ]
23193     }
23194 });
23195
23196  
23197
23198  
23199  /*
23200  * - LGPL
23201  *
23202  * CheckBox
23203  * 
23204  */
23205
23206 /**
23207  * @class Roo.bootstrap.CheckBox
23208  * @extends Roo.bootstrap.Input
23209  * Bootstrap CheckBox class
23210  * 
23211  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23212  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23213  * @cfg {String} boxLabel The text that appears beside the checkbox
23214  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23215  * @cfg {Boolean} checked initnal the element
23216  * @cfg {Boolean} inline inline the element (default false)
23217  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23218  * @cfg {String} tooltip label tooltip
23219  * 
23220  * @constructor
23221  * Create a new CheckBox
23222  * @param {Object} config The config object
23223  */
23224
23225 Roo.bootstrap.CheckBox = function(config){
23226     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23227    
23228     this.addEvents({
23229         /**
23230         * @event check
23231         * Fires when the element is checked or unchecked.
23232         * @param {Roo.bootstrap.CheckBox} this This input
23233         * @param {Boolean} checked The new checked value
23234         */
23235        check : true,
23236        /**
23237         * @event click
23238         * Fires when the element is click.
23239         * @param {Roo.bootstrap.CheckBox} this This input
23240         */
23241        click : true
23242     });
23243     
23244 };
23245
23246 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23247   
23248     inputType: 'checkbox',
23249     inputValue: 1,
23250     valueOff: 0,
23251     boxLabel: false,
23252     checked: false,
23253     weight : false,
23254     inline: false,
23255     tooltip : '',
23256     
23257     // checkbox success does not make any sense really.. 
23258     invalidClass : "",
23259     validClass : "",
23260     
23261     
23262     getAutoCreate : function()
23263     {
23264         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23265         
23266         var id = Roo.id();
23267         
23268         var cfg = {};
23269         
23270         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23271         
23272         if(this.inline){
23273             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23274         }
23275         
23276         var input =  {
23277             tag: 'input',
23278             id : id,
23279             type : this.inputType,
23280             value : this.inputValue,
23281             cls : 'roo-' + this.inputType, //'form-box',
23282             placeholder : this.placeholder || ''
23283             
23284         };
23285         
23286         if(this.inputType != 'radio'){
23287             var hidden =  {
23288                 tag: 'input',
23289                 type : 'hidden',
23290                 cls : 'roo-hidden-value',
23291                 value : this.checked ? this.inputValue : this.valueOff
23292             };
23293         }
23294         
23295             
23296         if (this.weight) { // Validity check?
23297             cfg.cls += " " + this.inputType + "-" + this.weight;
23298         }
23299         
23300         if (this.disabled) {
23301             input.disabled=true;
23302         }
23303         
23304         if(this.checked){
23305             input.checked = this.checked;
23306         }
23307         
23308         if (this.name) {
23309             
23310             input.name = this.name;
23311             
23312             if(this.inputType != 'radio'){
23313                 hidden.name = this.name;
23314                 input.name = '_hidden_' + this.name;
23315             }
23316         }
23317         
23318         if (this.size) {
23319             input.cls += ' input-' + this.size;
23320         }
23321         
23322         var settings=this;
23323         
23324         ['xs','sm','md','lg'].map(function(size){
23325             if (settings[size]) {
23326                 cfg.cls += ' col-' + size + '-' + settings[size];
23327             }
23328         });
23329         
23330         var inputblock = input;
23331          
23332         if (this.before || this.after) {
23333             
23334             inputblock = {
23335                 cls : 'input-group',
23336                 cn :  [] 
23337             };
23338             
23339             if (this.before) {
23340                 inputblock.cn.push({
23341                     tag :'span',
23342                     cls : 'input-group-addon',
23343                     html : this.before
23344                 });
23345             }
23346             
23347             inputblock.cn.push(input);
23348             
23349             if(this.inputType != 'radio'){
23350                 inputblock.cn.push(hidden);
23351             }
23352             
23353             if (this.after) {
23354                 inputblock.cn.push({
23355                     tag :'span',
23356                     cls : 'input-group-addon',
23357                     html : this.after
23358                 });
23359             }
23360             
23361         }
23362         var boxLabelCfg = false;
23363         
23364         if(this.boxLabel){
23365            
23366             boxLabelCfg = {
23367                 tag: 'label',
23368                 //'for': id, // box label is handled by onclick - so no for...
23369                 cls: 'box-label',
23370                 html: this.boxLabel
23371             };
23372             if(this.tooltip){
23373                 boxLabelCfg.tooltip = this.tooltip;
23374             }
23375              
23376         }
23377         
23378         
23379         if (align ==='left' && this.fieldLabel.length) {
23380 //                Roo.log("left and has label");
23381             cfg.cn = [
23382                 {
23383                     tag: 'label',
23384                     'for' :  id,
23385                     cls : 'control-label',
23386                     html : this.fieldLabel
23387                 },
23388                 {
23389                     cls : "", 
23390                     cn: [
23391                         inputblock
23392                     ]
23393                 }
23394             ];
23395             
23396             if (boxLabelCfg) {
23397                 cfg.cn[1].cn.push(boxLabelCfg);
23398             }
23399             
23400             if(this.labelWidth > 12){
23401                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23402             }
23403             
23404             if(this.labelWidth < 13 && this.labelmd == 0){
23405                 this.labelmd = this.labelWidth;
23406             }
23407             
23408             if(this.labellg > 0){
23409                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23410                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23411             }
23412             
23413             if(this.labelmd > 0){
23414                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23415                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23416             }
23417             
23418             if(this.labelsm > 0){
23419                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23420                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23421             }
23422             
23423             if(this.labelxs > 0){
23424                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23425                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23426             }
23427             
23428         } else if ( this.fieldLabel.length) {
23429 //                Roo.log(" label");
23430                 cfg.cn = [
23431                    
23432                     {
23433                         tag: this.boxLabel ? 'span' : 'label',
23434                         'for': id,
23435                         cls: 'control-label box-input-label',
23436                         //cls : 'input-group-addon',
23437                         html : this.fieldLabel
23438                     },
23439                     
23440                     inputblock
23441                     
23442                 ];
23443                 if (boxLabelCfg) {
23444                     cfg.cn.push(boxLabelCfg);
23445                 }
23446
23447         } else {
23448             
23449 //                Roo.log(" no label && no align");
23450                 cfg.cn = [  inputblock ] ;
23451                 if (boxLabelCfg) {
23452                     cfg.cn.push(boxLabelCfg);
23453                 }
23454
23455                 
23456         }
23457         
23458        
23459         
23460         if(this.inputType != 'radio'){
23461             cfg.cn.push(hidden);
23462         }
23463         
23464         return cfg;
23465         
23466     },
23467     
23468     /**
23469      * return the real input element.
23470      */
23471     inputEl: function ()
23472     {
23473         return this.el.select('input.roo-' + this.inputType,true).first();
23474     },
23475     hiddenEl: function ()
23476     {
23477         return this.el.select('input.roo-hidden-value',true).first();
23478     },
23479     
23480     labelEl: function()
23481     {
23482         return this.el.select('label.control-label',true).first();
23483     },
23484     /* depricated... */
23485     
23486     label: function()
23487     {
23488         return this.labelEl();
23489     },
23490     
23491     boxLabelEl: function()
23492     {
23493         return this.el.select('label.box-label',true).first();
23494     },
23495     
23496     initEvents : function()
23497     {
23498 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23499         
23500         this.inputEl().on('click', this.onClick,  this);
23501         
23502         if (this.boxLabel) { 
23503             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23504         }
23505         
23506         this.startValue = this.getValue();
23507         
23508         if(this.groupId){
23509             Roo.bootstrap.CheckBox.register(this);
23510         }
23511     },
23512     
23513     onClick : function(e)
23514     {   
23515         if(this.fireEvent('click', this, e) !== false){
23516             this.setChecked(!this.checked);
23517         }
23518         
23519     },
23520     
23521     setChecked : function(state,suppressEvent)
23522     {
23523         this.startValue = this.getValue();
23524
23525         if(this.inputType == 'radio'){
23526             
23527             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23528                 e.dom.checked = false;
23529             });
23530             
23531             this.inputEl().dom.checked = true;
23532             
23533             this.inputEl().dom.value = this.inputValue;
23534             
23535             if(suppressEvent !== true){
23536                 this.fireEvent('check', this, true);
23537             }
23538             
23539             this.validate();
23540             
23541             return;
23542         }
23543         
23544         this.checked = state;
23545         
23546         this.inputEl().dom.checked = state;
23547         
23548         
23549         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23550         
23551         if(suppressEvent !== true){
23552             this.fireEvent('check', this, state);
23553         }
23554         
23555         this.validate();
23556     },
23557     
23558     getValue : function()
23559     {
23560         if(this.inputType == 'radio'){
23561             return this.getGroupValue();
23562         }
23563         
23564         return this.hiddenEl().dom.value;
23565         
23566     },
23567     
23568     getGroupValue : function()
23569     {
23570         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23571             return '';
23572         }
23573         
23574         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23575     },
23576     
23577     setValue : function(v,suppressEvent)
23578     {
23579         if(this.inputType == 'radio'){
23580             this.setGroupValue(v, suppressEvent);
23581             return;
23582         }
23583         
23584         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23585         
23586         this.validate();
23587     },
23588     
23589     setGroupValue : function(v, suppressEvent)
23590     {
23591         this.startValue = this.getValue();
23592         
23593         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23594             e.dom.checked = false;
23595             
23596             if(e.dom.value == v){
23597                 e.dom.checked = true;
23598             }
23599         });
23600         
23601         if(suppressEvent !== true){
23602             this.fireEvent('check', this, true);
23603         }
23604
23605         this.validate();
23606         
23607         return;
23608     },
23609     
23610     validate : function()
23611     {
23612         if(this.getVisibilityEl().hasClass('hidden')){
23613             return true;
23614         }
23615         
23616         if(
23617                 this.disabled || 
23618                 (this.inputType == 'radio' && this.validateRadio()) ||
23619                 (this.inputType == 'checkbox' && this.validateCheckbox())
23620         ){
23621             this.markValid();
23622             return true;
23623         }
23624         
23625         this.markInvalid();
23626         return false;
23627     },
23628     
23629     validateRadio : function()
23630     {
23631         if(this.getVisibilityEl().hasClass('hidden')){
23632             return true;
23633         }
23634         
23635         if(this.allowBlank){
23636             return true;
23637         }
23638         
23639         var valid = false;
23640         
23641         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23642             if(!e.dom.checked){
23643                 return;
23644             }
23645             
23646             valid = true;
23647             
23648             return false;
23649         });
23650         
23651         return valid;
23652     },
23653     
23654     validateCheckbox : function()
23655     {
23656         if(!this.groupId){
23657             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23658             //return (this.getValue() == this.inputValue) ? true : false;
23659         }
23660         
23661         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23662         
23663         if(!group){
23664             return false;
23665         }
23666         
23667         var r = false;
23668         
23669         for(var i in group){
23670             if(group[i].el.isVisible(true)){
23671                 r = false;
23672                 break;
23673             }
23674             
23675             r = true;
23676         }
23677         
23678         for(var i in group){
23679             if(r){
23680                 break;
23681             }
23682             
23683             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23684         }
23685         
23686         return r;
23687     },
23688     
23689     /**
23690      * Mark this field as valid
23691      */
23692     markValid : function()
23693     {
23694         var _this = this;
23695         
23696         this.fireEvent('valid', this);
23697         
23698         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23699         
23700         if(this.groupId){
23701             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23702         }
23703         
23704         if(label){
23705             label.markValid();
23706         }
23707
23708         if(this.inputType == 'radio'){
23709             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23710                 var fg = e.findParent('.form-group', false, true);
23711                 if (Roo.bootstrap.version == 3) {
23712                     fg.removeClass([_this.invalidClass, _this.validClass]);
23713                     fg.addClass(_this.validClass);
23714                 } else {
23715                     fg.removeClass(['is-valid', 'is-invalid']);
23716                     fg.addClass('is-valid');
23717                 }
23718             });
23719             
23720             return;
23721         }
23722
23723         if(!this.groupId){
23724             var fg = this.el.findParent('.form-group', false, true);
23725             if (Roo.bootstrap.version == 3) {
23726                 fg.removeClass([this.invalidClass, this.validClass]);
23727                 fg.addClass(this.validClass);
23728             } else {
23729                 fg.removeClass(['is-valid', 'is-invalid']);
23730                 fg.addClass('is-valid');
23731             }
23732             return;
23733         }
23734         
23735         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23736         
23737         if(!group){
23738             return;
23739         }
23740         
23741         for(var i in group){
23742             var fg = group[i].el.findParent('.form-group', false, true);
23743             if (Roo.bootstrap.version == 3) {
23744                 fg.removeClass([this.invalidClass, this.validClass]);
23745                 fg.addClass(this.validClass);
23746             } else {
23747                 fg.removeClass(['is-valid', 'is-invalid']);
23748                 fg.addClass('is-valid');
23749             }
23750         }
23751     },
23752     
23753      /**
23754      * Mark this field as invalid
23755      * @param {String} msg The validation message
23756      */
23757     markInvalid : function(msg)
23758     {
23759         if(this.allowBlank){
23760             return;
23761         }
23762         
23763         var _this = this;
23764         
23765         this.fireEvent('invalid', this, msg);
23766         
23767         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23768         
23769         if(this.groupId){
23770             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23771         }
23772         
23773         if(label){
23774             label.markInvalid();
23775         }
23776             
23777         if(this.inputType == 'radio'){
23778             
23779             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23780                 var fg = e.findParent('.form-group', false, true);
23781                 if (Roo.bootstrap.version == 3) {
23782                     fg.removeClass([_this.invalidClass, _this.validClass]);
23783                     fg.addClass(_this.invalidClass);
23784                 } else {
23785                     fg.removeClass(['is-invalid', 'is-valid']);
23786                     fg.addClass('is-invalid');
23787                 }
23788             });
23789             
23790             return;
23791         }
23792         
23793         if(!this.groupId){
23794             var fg = this.el.findParent('.form-group', false, true);
23795             if (Roo.bootstrap.version == 3) {
23796                 fg.removeClass([_this.invalidClass, _this.validClass]);
23797                 fg.addClass(_this.invalidClass);
23798             } else {
23799                 fg.removeClass(['is-invalid', 'is-valid']);
23800                 fg.addClass('is-invalid');
23801             }
23802             return;
23803         }
23804         
23805         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23806         
23807         if(!group){
23808             return;
23809         }
23810         
23811         for(var i in group){
23812             var fg = group[i].el.findParent('.form-group', false, true);
23813             if (Roo.bootstrap.version == 3) {
23814                 fg.removeClass([_this.invalidClass, _this.validClass]);
23815                 fg.addClass(_this.invalidClass);
23816             } else {
23817                 fg.removeClass(['is-invalid', 'is-valid']);
23818                 fg.addClass('is-invalid');
23819             }
23820         }
23821         
23822     },
23823     
23824     clearInvalid : function()
23825     {
23826         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23827         
23828         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23829         
23830         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23831         
23832         if (label && label.iconEl) {
23833             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23834             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23835         }
23836     },
23837     
23838     disable : function()
23839     {
23840         if(this.inputType != 'radio'){
23841             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23842             return;
23843         }
23844         
23845         var _this = this;
23846         
23847         if(this.rendered){
23848             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23849                 _this.getActionEl().addClass(this.disabledClass);
23850                 e.dom.disabled = true;
23851             });
23852         }
23853         
23854         this.disabled = true;
23855         this.fireEvent("disable", this);
23856         return this;
23857     },
23858
23859     enable : function()
23860     {
23861         if(this.inputType != 'radio'){
23862             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23863             return;
23864         }
23865         
23866         var _this = this;
23867         
23868         if(this.rendered){
23869             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23870                 _this.getActionEl().removeClass(this.disabledClass);
23871                 e.dom.disabled = false;
23872             });
23873         }
23874         
23875         this.disabled = false;
23876         this.fireEvent("enable", this);
23877         return this;
23878     },
23879     
23880     setBoxLabel : function(v)
23881     {
23882         this.boxLabel = v;
23883         
23884         if(this.rendered){
23885             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23886         }
23887     }
23888
23889 });
23890
23891 Roo.apply(Roo.bootstrap.CheckBox, {
23892     
23893     groups: {},
23894     
23895      /**
23896     * register a CheckBox Group
23897     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23898     */
23899     register : function(checkbox)
23900     {
23901         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23902             this.groups[checkbox.groupId] = {};
23903         }
23904         
23905         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23906             return;
23907         }
23908         
23909         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23910         
23911     },
23912     /**
23913     * fetch a CheckBox Group based on the group ID
23914     * @param {string} the group ID
23915     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23916     */
23917     get: function(groupId) {
23918         if (typeof(this.groups[groupId]) == 'undefined') {
23919             return false;
23920         }
23921         
23922         return this.groups[groupId] ;
23923     }
23924     
23925     
23926 });
23927 /*
23928  * - LGPL
23929  *
23930  * RadioItem
23931  * 
23932  */
23933
23934 /**
23935  * @class Roo.bootstrap.Radio
23936  * @extends Roo.bootstrap.Component
23937  * Bootstrap Radio class
23938  * @cfg {String} boxLabel - the label associated
23939  * @cfg {String} value - the value of radio
23940  * 
23941  * @constructor
23942  * Create a new Radio
23943  * @param {Object} config The config object
23944  */
23945 Roo.bootstrap.Radio = function(config){
23946     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23947     
23948 };
23949
23950 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23951     
23952     boxLabel : '',
23953     
23954     value : '',
23955     
23956     getAutoCreate : function()
23957     {
23958         var cfg = {
23959             tag : 'div',
23960             cls : 'form-group radio',
23961             cn : [
23962                 {
23963                     tag : 'label',
23964                     cls : 'box-label',
23965                     html : this.boxLabel
23966                 }
23967             ]
23968         };
23969         
23970         return cfg;
23971     },
23972     
23973     initEvents : function() 
23974     {
23975         this.parent().register(this);
23976         
23977         this.el.on('click', this.onClick, this);
23978         
23979     },
23980     
23981     onClick : function(e)
23982     {
23983         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23984             this.setChecked(true);
23985         }
23986     },
23987     
23988     setChecked : function(state, suppressEvent)
23989     {
23990         this.parent().setValue(this.value, suppressEvent);
23991         
23992     },
23993     
23994     setBoxLabel : function(v)
23995     {
23996         this.boxLabel = v;
23997         
23998         if(this.rendered){
23999             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24000         }
24001     }
24002     
24003 });
24004  
24005
24006  /*
24007  * - LGPL
24008  *
24009  * Input
24010  * 
24011  */
24012
24013 /**
24014  * @class Roo.bootstrap.SecurePass
24015  * @extends Roo.bootstrap.Input
24016  * Bootstrap SecurePass class
24017  *
24018  * 
24019  * @constructor
24020  * Create a new SecurePass
24021  * @param {Object} config The config object
24022  */
24023  
24024 Roo.bootstrap.SecurePass = function (config) {
24025     // these go here, so the translation tool can replace them..
24026     this.errors = {
24027         PwdEmpty: "Please type a password, and then retype it to confirm.",
24028         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24029         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24030         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24031         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24032         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24033         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24034         TooWeak: "Your password is Too Weak."
24035     },
24036     this.meterLabel = "Password strength:";
24037     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24038     this.meterClass = [
24039         "roo-password-meter-tooweak", 
24040         "roo-password-meter-weak", 
24041         "roo-password-meter-medium", 
24042         "roo-password-meter-strong", 
24043         "roo-password-meter-grey"
24044     ];
24045     
24046     this.errors = {};
24047     
24048     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24049 }
24050
24051 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24052     /**
24053      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24054      * {
24055      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24056      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24057      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24058      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24059      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24060      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24061      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24062      * })
24063      */
24064     // private
24065     
24066     meterWidth: 300,
24067     errorMsg :'',    
24068     errors: false,
24069     imageRoot: '/',
24070     /**
24071      * @cfg {String/Object} Label for the strength meter (defaults to
24072      * 'Password strength:')
24073      */
24074     // private
24075     meterLabel: '',
24076     /**
24077      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24078      * ['Weak', 'Medium', 'Strong'])
24079      */
24080     // private    
24081     pwdStrengths: false,    
24082     // private
24083     strength: 0,
24084     // private
24085     _lastPwd: null,
24086     // private
24087     kCapitalLetter: 0,
24088     kSmallLetter: 1,
24089     kDigit: 2,
24090     kPunctuation: 3,
24091     
24092     insecure: false,
24093     // private
24094     initEvents: function ()
24095     {
24096         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24097
24098         if (this.el.is('input[type=password]') && Roo.isSafari) {
24099             this.el.on('keydown', this.SafariOnKeyDown, this);
24100         }
24101
24102         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24103     },
24104     // private
24105     onRender: function (ct, position)
24106     {
24107         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24108         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24109         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24110
24111         this.trigger.createChild({
24112                    cn: [
24113                     {
24114                     //id: 'PwdMeter',
24115                     tag: 'div',
24116                     cls: 'roo-password-meter-grey col-xs-12',
24117                     style: {
24118                         //width: 0,
24119                         //width: this.meterWidth + 'px'                                                
24120                         }
24121                     },
24122                     {                            
24123                          cls: 'roo-password-meter-text'                          
24124                     }
24125                 ]            
24126         });
24127
24128          
24129         if (this.hideTrigger) {
24130             this.trigger.setDisplayed(false);
24131         }
24132         this.setSize(this.width || '', this.height || '');
24133     },
24134     // private
24135     onDestroy: function ()
24136     {
24137         if (this.trigger) {
24138             this.trigger.removeAllListeners();
24139             this.trigger.remove();
24140         }
24141         if (this.wrap) {
24142             this.wrap.remove();
24143         }
24144         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24145     },
24146     // private
24147     checkStrength: function ()
24148     {
24149         var pwd = this.inputEl().getValue();
24150         if (pwd == this._lastPwd) {
24151             return;
24152         }
24153
24154         var strength;
24155         if (this.ClientSideStrongPassword(pwd)) {
24156             strength = 3;
24157         } else if (this.ClientSideMediumPassword(pwd)) {
24158             strength = 2;
24159         } else if (this.ClientSideWeakPassword(pwd)) {
24160             strength = 1;
24161         } else {
24162             strength = 0;
24163         }
24164         
24165         Roo.log('strength1: ' + strength);
24166         
24167         //var pm = this.trigger.child('div/div/div').dom;
24168         var pm = this.trigger.child('div/div');
24169         pm.removeClass(this.meterClass);
24170         pm.addClass(this.meterClass[strength]);
24171                 
24172         
24173         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24174                 
24175         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24176         
24177         this._lastPwd = pwd;
24178     },
24179     reset: function ()
24180     {
24181         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24182         
24183         this._lastPwd = '';
24184         
24185         var pm = this.trigger.child('div/div');
24186         pm.removeClass(this.meterClass);
24187         pm.addClass('roo-password-meter-grey');        
24188         
24189         
24190         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24191         
24192         pt.innerHTML = '';
24193         this.inputEl().dom.type='password';
24194     },
24195     // private
24196     validateValue: function (value)
24197     {
24198         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24199             return false;
24200         }
24201         if (value.length == 0) {
24202             if (this.allowBlank) {
24203                 this.clearInvalid();
24204                 return true;
24205             }
24206
24207             this.markInvalid(this.errors.PwdEmpty);
24208             this.errorMsg = this.errors.PwdEmpty;
24209             return false;
24210         }
24211         
24212         if(this.insecure){
24213             return true;
24214         }
24215         
24216         if (!value.match(/[\x21-\x7e]+/)) {
24217             this.markInvalid(this.errors.PwdBadChar);
24218             this.errorMsg = this.errors.PwdBadChar;
24219             return false;
24220         }
24221         if (value.length < 6) {
24222             this.markInvalid(this.errors.PwdShort);
24223             this.errorMsg = this.errors.PwdShort;
24224             return false;
24225         }
24226         if (value.length > 16) {
24227             this.markInvalid(this.errors.PwdLong);
24228             this.errorMsg = this.errors.PwdLong;
24229             return false;
24230         }
24231         var strength;
24232         if (this.ClientSideStrongPassword(value)) {
24233             strength = 3;
24234         } else if (this.ClientSideMediumPassword(value)) {
24235             strength = 2;
24236         } else if (this.ClientSideWeakPassword(value)) {
24237             strength = 1;
24238         } else {
24239             strength = 0;
24240         }
24241
24242         
24243         if (strength < 2) {
24244             //this.markInvalid(this.errors.TooWeak);
24245             this.errorMsg = this.errors.TooWeak;
24246             //return false;
24247         }
24248         
24249         
24250         console.log('strength2: ' + strength);
24251         
24252         //var pm = this.trigger.child('div/div/div').dom;
24253         
24254         var pm = this.trigger.child('div/div');
24255         pm.removeClass(this.meterClass);
24256         pm.addClass(this.meterClass[strength]);
24257                 
24258         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24259                 
24260         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24261         
24262         this.errorMsg = ''; 
24263         return true;
24264     },
24265     // private
24266     CharacterSetChecks: function (type)
24267     {
24268         this.type = type;
24269         this.fResult = false;
24270     },
24271     // private
24272     isctype: function (character, type)
24273     {
24274         switch (type) {  
24275             case this.kCapitalLetter:
24276                 if (character >= 'A' && character <= 'Z') {
24277                     return true;
24278                 }
24279                 break;
24280             
24281             case this.kSmallLetter:
24282                 if (character >= 'a' && character <= 'z') {
24283                     return true;
24284                 }
24285                 break;
24286             
24287             case this.kDigit:
24288                 if (character >= '0' && character <= '9') {
24289                     return true;
24290                 }
24291                 break;
24292             
24293             case this.kPunctuation:
24294                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24295                     return true;
24296                 }
24297                 break;
24298             
24299             default:
24300                 return false;
24301         }
24302
24303     },
24304     // private
24305     IsLongEnough: function (pwd, size)
24306     {
24307         return !(pwd == null || isNaN(size) || pwd.length < size);
24308     },
24309     // private
24310     SpansEnoughCharacterSets: function (word, nb)
24311     {
24312         if (!this.IsLongEnough(word, nb))
24313         {
24314             return false;
24315         }
24316
24317         var characterSetChecks = new Array(
24318             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24319             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24320         );
24321         
24322         for (var index = 0; index < word.length; ++index) {
24323             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24324                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24325                     characterSetChecks[nCharSet].fResult = true;
24326                     break;
24327                 }
24328             }
24329         }
24330
24331         var nCharSets = 0;
24332         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24333             if (characterSetChecks[nCharSet].fResult) {
24334                 ++nCharSets;
24335             }
24336         }
24337
24338         if (nCharSets < nb) {
24339             return false;
24340         }
24341         return true;
24342     },
24343     // private
24344     ClientSideStrongPassword: function (pwd)
24345     {
24346         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24347     },
24348     // private
24349     ClientSideMediumPassword: function (pwd)
24350     {
24351         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24352     },
24353     // private
24354     ClientSideWeakPassword: function (pwd)
24355     {
24356         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24357     }
24358           
24359 })//<script type="text/javascript">
24360
24361 /*
24362  * Based  Ext JS Library 1.1.1
24363  * Copyright(c) 2006-2007, Ext JS, LLC.
24364  * LGPL
24365  *
24366  */
24367  
24368 /**
24369  * @class Roo.HtmlEditorCore
24370  * @extends Roo.Component
24371  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24372  *
24373  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24374  */
24375
24376 Roo.HtmlEditorCore = function(config){
24377     
24378     
24379     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24380     
24381     
24382     this.addEvents({
24383         /**
24384          * @event initialize
24385          * Fires when the editor is fully initialized (including the iframe)
24386          * @param {Roo.HtmlEditorCore} this
24387          */
24388         initialize: true,
24389         /**
24390          * @event activate
24391          * Fires when the editor is first receives the focus. Any insertion must wait
24392          * until after this event.
24393          * @param {Roo.HtmlEditorCore} this
24394          */
24395         activate: true,
24396          /**
24397          * @event beforesync
24398          * Fires before the textarea is updated with content from the editor iframe. Return false
24399          * to cancel the sync.
24400          * @param {Roo.HtmlEditorCore} this
24401          * @param {String} html
24402          */
24403         beforesync: true,
24404          /**
24405          * @event beforepush
24406          * Fires before the iframe editor is updated with content from the textarea. Return false
24407          * to cancel the push.
24408          * @param {Roo.HtmlEditorCore} this
24409          * @param {String} html
24410          */
24411         beforepush: true,
24412          /**
24413          * @event sync
24414          * Fires when the textarea is updated with content from the editor iframe.
24415          * @param {Roo.HtmlEditorCore} this
24416          * @param {String} html
24417          */
24418         sync: true,
24419          /**
24420          * @event push
24421          * Fires when the iframe editor is updated with content from the textarea.
24422          * @param {Roo.HtmlEditorCore} this
24423          * @param {String} html
24424          */
24425         push: true,
24426         
24427         /**
24428          * @event editorevent
24429          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24430          * @param {Roo.HtmlEditorCore} this
24431          */
24432         editorevent: true
24433         
24434     });
24435     
24436     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24437     
24438     // defaults : white / black...
24439     this.applyBlacklists();
24440     
24441     
24442     
24443 };
24444
24445
24446 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24447
24448
24449      /**
24450      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24451      */
24452     
24453     owner : false,
24454     
24455      /**
24456      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24457      *                        Roo.resizable.
24458      */
24459     resizable : false,
24460      /**
24461      * @cfg {Number} height (in pixels)
24462      */   
24463     height: 300,
24464    /**
24465      * @cfg {Number} width (in pixels)
24466      */   
24467     width: 500,
24468     
24469     /**
24470      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24471      * 
24472      */
24473     stylesheets: false,
24474     
24475     // id of frame..
24476     frameId: false,
24477     
24478     // private properties
24479     validationEvent : false,
24480     deferHeight: true,
24481     initialized : false,
24482     activated : false,
24483     sourceEditMode : false,
24484     onFocus : Roo.emptyFn,
24485     iframePad:3,
24486     hideMode:'offsets',
24487     
24488     clearUp: true,
24489     
24490     // blacklist + whitelisted elements..
24491     black: false,
24492     white: false,
24493      
24494     bodyCls : '',
24495
24496     /**
24497      * Protected method that will not generally be called directly. It
24498      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24499      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24500      */
24501     getDocMarkup : function(){
24502         // body styles..
24503         var st = '';
24504         
24505         // inherit styels from page...?? 
24506         if (this.stylesheets === false) {
24507             
24508             Roo.get(document.head).select('style').each(function(node) {
24509                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24510             });
24511             
24512             Roo.get(document.head).select('link').each(function(node) { 
24513                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24514             });
24515             
24516         } else if (!this.stylesheets.length) {
24517                 // simple..
24518                 st = '<style type="text/css">' +
24519                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24520                    '</style>';
24521         } else {
24522             for (var i in this.stylesheets) { 
24523                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24524             }
24525             
24526         }
24527         
24528         st +=  '<style type="text/css">' +
24529             'IMG { cursor: pointer } ' +
24530         '</style>';
24531
24532         var cls = 'roo-htmleditor-body';
24533         
24534         if(this.bodyCls.length){
24535             cls += ' ' + this.bodyCls;
24536         }
24537         
24538         return '<html><head>' + st  +
24539             //<style type="text/css">' +
24540             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24541             //'</style>' +
24542             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24543     },
24544
24545     // private
24546     onRender : function(ct, position)
24547     {
24548         var _t = this;
24549         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24550         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24551         
24552         
24553         this.el.dom.style.border = '0 none';
24554         this.el.dom.setAttribute('tabIndex', -1);
24555         this.el.addClass('x-hidden hide');
24556         
24557         
24558         
24559         if(Roo.isIE){ // fix IE 1px bogus margin
24560             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24561         }
24562        
24563         
24564         this.frameId = Roo.id();
24565         
24566          
24567         
24568         var iframe = this.owner.wrap.createChild({
24569             tag: 'iframe',
24570             cls: 'form-control', // bootstrap..
24571             id: this.frameId,
24572             name: this.frameId,
24573             frameBorder : 'no',
24574             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24575         }, this.el
24576         );
24577         
24578         
24579         this.iframe = iframe.dom;
24580
24581          this.assignDocWin();
24582         
24583         this.doc.designMode = 'on';
24584        
24585         this.doc.open();
24586         this.doc.write(this.getDocMarkup());
24587         this.doc.close();
24588
24589         
24590         var task = { // must defer to wait for browser to be ready
24591             run : function(){
24592                 //console.log("run task?" + this.doc.readyState);
24593                 this.assignDocWin();
24594                 if(this.doc.body || this.doc.readyState == 'complete'){
24595                     try {
24596                         this.doc.designMode="on";
24597                     } catch (e) {
24598                         return;
24599                     }
24600                     Roo.TaskMgr.stop(task);
24601                     this.initEditor.defer(10, this);
24602                 }
24603             },
24604             interval : 10,
24605             duration: 10000,
24606             scope: this
24607         };
24608         Roo.TaskMgr.start(task);
24609
24610     },
24611
24612     // private
24613     onResize : function(w, h)
24614     {
24615          Roo.log('resize: ' +w + ',' + h );
24616         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24617         if(!this.iframe){
24618             return;
24619         }
24620         if(typeof w == 'number'){
24621             
24622             this.iframe.style.width = w + 'px';
24623         }
24624         if(typeof h == 'number'){
24625             
24626             this.iframe.style.height = h + 'px';
24627             if(this.doc){
24628                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24629             }
24630         }
24631         
24632     },
24633
24634     /**
24635      * Toggles the editor between standard and source edit mode.
24636      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24637      */
24638     toggleSourceEdit : function(sourceEditMode){
24639         
24640         this.sourceEditMode = sourceEditMode === true;
24641         
24642         if(this.sourceEditMode){
24643  
24644             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24645             
24646         }else{
24647             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24648             //this.iframe.className = '';
24649             this.deferFocus();
24650         }
24651         //this.setSize(this.owner.wrap.getSize());
24652         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24653     },
24654
24655     
24656   
24657
24658     /**
24659      * Protected method that will not generally be called directly. If you need/want
24660      * custom HTML cleanup, this is the method you should override.
24661      * @param {String} html The HTML to be cleaned
24662      * return {String} The cleaned HTML
24663      */
24664     cleanHtml : function(html){
24665         html = String(html);
24666         if(html.length > 5){
24667             if(Roo.isSafari){ // strip safari nonsense
24668                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24669             }
24670         }
24671         if(html == '&nbsp;'){
24672             html = '';
24673         }
24674         return html;
24675     },
24676
24677     /**
24678      * HTML Editor -> Textarea
24679      * Protected method that will not generally be called directly. Syncs the contents
24680      * of the editor iframe with the textarea.
24681      */
24682     syncValue : function(){
24683         if(this.initialized){
24684             var bd = (this.doc.body || this.doc.documentElement);
24685             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24686             var html = bd.innerHTML;
24687             if(Roo.isSafari){
24688                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24689                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24690                 if(m && m[1]){
24691                     html = '<div style="'+m[0]+'">' + html + '</div>';
24692                 }
24693             }
24694             html = this.cleanHtml(html);
24695             // fix up the special chars.. normaly like back quotes in word...
24696             // however we do not want to do this with chinese..
24697             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24698                 
24699                 var cc = match.charCodeAt();
24700
24701                 // Get the character value, handling surrogate pairs
24702                 if (match.length == 2) {
24703                     // It's a surrogate pair, calculate the Unicode code point
24704                     var high = match.charCodeAt(0) - 0xD800;
24705                     var low  = match.charCodeAt(1) - 0xDC00;
24706                     cc = (high * 0x400) + low + 0x10000;
24707                 }  else if (
24708                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24709                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24710                     (cc >= 0xf900 && cc < 0xfb00 )
24711                 ) {
24712                         return match;
24713                 }  
24714          
24715                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24716                 return "&#" + cc + ";";
24717                 
24718                 
24719             });
24720             
24721             
24722              
24723             if(this.owner.fireEvent('beforesync', this, html) !== false){
24724                 this.el.dom.value = html;
24725                 this.owner.fireEvent('sync', this, html);
24726             }
24727         }
24728     },
24729
24730     /**
24731      * Protected method that will not generally be called directly. Pushes the value of the textarea
24732      * into the iframe editor.
24733      */
24734     pushValue : function(){
24735         if(this.initialized){
24736             var v = this.el.dom.value.trim();
24737             
24738 //            if(v.length < 1){
24739 //                v = '&#160;';
24740 //            }
24741             
24742             if(this.owner.fireEvent('beforepush', this, v) !== false){
24743                 var d = (this.doc.body || this.doc.documentElement);
24744                 d.innerHTML = v;
24745                 this.cleanUpPaste();
24746                 this.el.dom.value = d.innerHTML;
24747                 this.owner.fireEvent('push', this, v);
24748             }
24749         }
24750     },
24751
24752     // private
24753     deferFocus : function(){
24754         this.focus.defer(10, this);
24755     },
24756
24757     // doc'ed in Field
24758     focus : function(){
24759         if(this.win && !this.sourceEditMode){
24760             this.win.focus();
24761         }else{
24762             this.el.focus();
24763         }
24764     },
24765     
24766     assignDocWin: function()
24767     {
24768         var iframe = this.iframe;
24769         
24770          if(Roo.isIE){
24771             this.doc = iframe.contentWindow.document;
24772             this.win = iframe.contentWindow;
24773         } else {
24774 //            if (!Roo.get(this.frameId)) {
24775 //                return;
24776 //            }
24777 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24778 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24779             
24780             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24781                 return;
24782             }
24783             
24784             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24785             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24786         }
24787     },
24788     
24789     // private
24790     initEditor : function(){
24791         //console.log("INIT EDITOR");
24792         this.assignDocWin();
24793         
24794         
24795         
24796         this.doc.designMode="on";
24797         this.doc.open();
24798         this.doc.write(this.getDocMarkup());
24799         this.doc.close();
24800         
24801         var dbody = (this.doc.body || this.doc.documentElement);
24802         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24803         // this copies styles from the containing element into thsi one..
24804         // not sure why we need all of this..
24805         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24806         
24807         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24808         //ss['background-attachment'] = 'fixed'; // w3c
24809         dbody.bgProperties = 'fixed'; // ie
24810         //Roo.DomHelper.applyStyles(dbody, ss);
24811         Roo.EventManager.on(this.doc, {
24812             //'mousedown': this.onEditorEvent,
24813             'mouseup': this.onEditorEvent,
24814             'dblclick': this.onEditorEvent,
24815             'click': this.onEditorEvent,
24816             'keyup': this.onEditorEvent,
24817             buffer:100,
24818             scope: this
24819         });
24820         if(Roo.isGecko){
24821             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24822         }
24823         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24824             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24825         }
24826         this.initialized = true;
24827
24828         this.owner.fireEvent('initialize', this);
24829         this.pushValue();
24830     },
24831
24832     // private
24833     onDestroy : function(){
24834         
24835         
24836         
24837         if(this.rendered){
24838             
24839             //for (var i =0; i < this.toolbars.length;i++) {
24840             //    // fixme - ask toolbars for heights?
24841             //    this.toolbars[i].onDestroy();
24842            // }
24843             
24844             //this.wrap.dom.innerHTML = '';
24845             //this.wrap.remove();
24846         }
24847     },
24848
24849     // private
24850     onFirstFocus : function(){
24851         
24852         this.assignDocWin();
24853         
24854         
24855         this.activated = true;
24856          
24857     
24858         if(Roo.isGecko){ // prevent silly gecko errors
24859             this.win.focus();
24860             var s = this.win.getSelection();
24861             if(!s.focusNode || s.focusNode.nodeType != 3){
24862                 var r = s.getRangeAt(0);
24863                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24864                 r.collapse(true);
24865                 this.deferFocus();
24866             }
24867             try{
24868                 this.execCmd('useCSS', true);
24869                 this.execCmd('styleWithCSS', false);
24870             }catch(e){}
24871         }
24872         this.owner.fireEvent('activate', this);
24873     },
24874
24875     // private
24876     adjustFont: function(btn){
24877         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24878         //if(Roo.isSafari){ // safari
24879         //    adjust *= 2;
24880        // }
24881         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24882         if(Roo.isSafari){ // safari
24883             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24884             v =  (v < 10) ? 10 : v;
24885             v =  (v > 48) ? 48 : v;
24886             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24887             
24888         }
24889         
24890         
24891         v = Math.max(1, v+adjust);
24892         
24893         this.execCmd('FontSize', v  );
24894     },
24895
24896     onEditorEvent : function(e)
24897     {
24898         this.owner.fireEvent('editorevent', this, e);
24899       //  this.updateToolbar();
24900         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24901     },
24902
24903     insertTag : function(tg)
24904     {
24905         // could be a bit smarter... -> wrap the current selected tRoo..
24906         if (tg.toLowerCase() == 'span' ||
24907             tg.toLowerCase() == 'code' ||
24908             tg.toLowerCase() == 'sup' ||
24909             tg.toLowerCase() == 'sub' 
24910             ) {
24911             
24912             range = this.createRange(this.getSelection());
24913             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24914             wrappingNode.appendChild(range.extractContents());
24915             range.insertNode(wrappingNode);
24916
24917             return;
24918             
24919             
24920             
24921         }
24922         this.execCmd("formatblock",   tg);
24923         
24924     },
24925     
24926     insertText : function(txt)
24927     {
24928         
24929         
24930         var range = this.createRange();
24931         range.deleteContents();
24932                //alert(Sender.getAttribute('label'));
24933                
24934         range.insertNode(this.doc.createTextNode(txt));
24935     } ,
24936     
24937      
24938
24939     /**
24940      * Executes a Midas editor command on the editor document and performs necessary focus and
24941      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24942      * @param {String} cmd The Midas command
24943      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24944      */
24945     relayCmd : function(cmd, value){
24946         this.win.focus();
24947         this.execCmd(cmd, value);
24948         this.owner.fireEvent('editorevent', this);
24949         //this.updateToolbar();
24950         this.owner.deferFocus();
24951     },
24952
24953     /**
24954      * Executes a Midas editor command directly on the editor document.
24955      * For visual commands, you should use {@link #relayCmd} instead.
24956      * <b>This should only be called after the editor is initialized.</b>
24957      * @param {String} cmd The Midas command
24958      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24959      */
24960     execCmd : function(cmd, value){
24961         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24962         this.syncValue();
24963     },
24964  
24965  
24966    
24967     /**
24968      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24969      * to insert tRoo.
24970      * @param {String} text | dom node.. 
24971      */
24972     insertAtCursor : function(text)
24973     {
24974         
24975         if(!this.activated){
24976             return;
24977         }
24978         /*
24979         if(Roo.isIE){
24980             this.win.focus();
24981             var r = this.doc.selection.createRange();
24982             if(r){
24983                 r.collapse(true);
24984                 r.pasteHTML(text);
24985                 this.syncValue();
24986                 this.deferFocus();
24987             
24988             }
24989             return;
24990         }
24991         */
24992         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24993             this.win.focus();
24994             
24995             
24996             // from jquery ui (MIT licenced)
24997             var range, node;
24998             var win = this.win;
24999             
25000             if (win.getSelection && win.getSelection().getRangeAt) {
25001                 range = win.getSelection().getRangeAt(0);
25002                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25003                 range.insertNode(node);
25004             } else if (win.document.selection && win.document.selection.createRange) {
25005                 // no firefox support
25006                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25007                 win.document.selection.createRange().pasteHTML(txt);
25008             } else {
25009                 // no firefox support
25010                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25011                 this.execCmd('InsertHTML', txt);
25012             } 
25013             
25014             this.syncValue();
25015             
25016             this.deferFocus();
25017         }
25018     },
25019  // private
25020     mozKeyPress : function(e){
25021         if(e.ctrlKey){
25022             var c = e.getCharCode(), cmd;
25023           
25024             if(c > 0){
25025                 c = String.fromCharCode(c).toLowerCase();
25026                 switch(c){
25027                     case 'b':
25028                         cmd = 'bold';
25029                         break;
25030                     case 'i':
25031                         cmd = 'italic';
25032                         break;
25033                     
25034                     case 'u':
25035                         cmd = 'underline';
25036                         break;
25037                     
25038                     case 'v':
25039                         this.cleanUpPaste.defer(100, this);
25040                         return;
25041                         
25042                 }
25043                 if(cmd){
25044                     this.win.focus();
25045                     this.execCmd(cmd);
25046                     this.deferFocus();
25047                     e.preventDefault();
25048                 }
25049                 
25050             }
25051         }
25052     },
25053
25054     // private
25055     fixKeys : function(){ // load time branching for fastest keydown performance
25056         if(Roo.isIE){
25057             return function(e){
25058                 var k = e.getKey(), r;
25059                 if(k == e.TAB){
25060                     e.stopEvent();
25061                     r = this.doc.selection.createRange();
25062                     if(r){
25063                         r.collapse(true);
25064                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25065                         this.deferFocus();
25066                     }
25067                     return;
25068                 }
25069                 
25070                 if(k == e.ENTER){
25071                     r = this.doc.selection.createRange();
25072                     if(r){
25073                         var target = r.parentElement();
25074                         if(!target || target.tagName.toLowerCase() != 'li'){
25075                             e.stopEvent();
25076                             r.pasteHTML('<br />');
25077                             r.collapse(false);
25078                             r.select();
25079                         }
25080                     }
25081                 }
25082                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25083                     this.cleanUpPaste.defer(100, this);
25084                     return;
25085                 }
25086                 
25087                 
25088             };
25089         }else if(Roo.isOpera){
25090             return function(e){
25091                 var k = e.getKey();
25092                 if(k == e.TAB){
25093                     e.stopEvent();
25094                     this.win.focus();
25095                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25096                     this.deferFocus();
25097                 }
25098                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25099                     this.cleanUpPaste.defer(100, this);
25100                     return;
25101                 }
25102                 
25103             };
25104         }else if(Roo.isSafari){
25105             return function(e){
25106                 var k = e.getKey();
25107                 
25108                 if(k == e.TAB){
25109                     e.stopEvent();
25110                     this.execCmd('InsertText','\t');
25111                     this.deferFocus();
25112                     return;
25113                 }
25114                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25115                     this.cleanUpPaste.defer(100, this);
25116                     return;
25117                 }
25118                 
25119              };
25120         }
25121     }(),
25122     
25123     getAllAncestors: function()
25124     {
25125         var p = this.getSelectedNode();
25126         var a = [];
25127         if (!p) {
25128             a.push(p); // push blank onto stack..
25129             p = this.getParentElement();
25130         }
25131         
25132         
25133         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25134             a.push(p);
25135             p = p.parentNode;
25136         }
25137         a.push(this.doc.body);
25138         return a;
25139     },
25140     lastSel : false,
25141     lastSelNode : false,
25142     
25143     
25144     getSelection : function() 
25145     {
25146         this.assignDocWin();
25147         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25148     },
25149     
25150     getSelectedNode: function() 
25151     {
25152         // this may only work on Gecko!!!
25153         
25154         // should we cache this!!!!
25155         
25156         
25157         
25158          
25159         var range = this.createRange(this.getSelection()).cloneRange();
25160         
25161         if (Roo.isIE) {
25162             var parent = range.parentElement();
25163             while (true) {
25164                 var testRange = range.duplicate();
25165                 testRange.moveToElementText(parent);
25166                 if (testRange.inRange(range)) {
25167                     break;
25168                 }
25169                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25170                     break;
25171                 }
25172                 parent = parent.parentElement;
25173             }
25174             return parent;
25175         }
25176         
25177         // is ancestor a text element.
25178         var ac =  range.commonAncestorContainer;
25179         if (ac.nodeType == 3) {
25180             ac = ac.parentNode;
25181         }
25182         
25183         var ar = ac.childNodes;
25184          
25185         var nodes = [];
25186         var other_nodes = [];
25187         var has_other_nodes = false;
25188         for (var i=0;i<ar.length;i++) {
25189             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25190                 continue;
25191             }
25192             // fullly contained node.
25193             
25194             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25195                 nodes.push(ar[i]);
25196                 continue;
25197             }
25198             
25199             // probably selected..
25200             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25201                 other_nodes.push(ar[i]);
25202                 continue;
25203             }
25204             // outer..
25205             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25206                 continue;
25207             }
25208             
25209             
25210             has_other_nodes = true;
25211         }
25212         if (!nodes.length && other_nodes.length) {
25213             nodes= other_nodes;
25214         }
25215         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25216             return false;
25217         }
25218         
25219         return nodes[0];
25220     },
25221     createRange: function(sel)
25222     {
25223         // this has strange effects when using with 
25224         // top toolbar - not sure if it's a great idea.
25225         //this.editor.contentWindow.focus();
25226         if (typeof sel != "undefined") {
25227             try {
25228                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25229             } catch(e) {
25230                 return this.doc.createRange();
25231             }
25232         } else {
25233             return this.doc.createRange();
25234         }
25235     },
25236     getParentElement: function()
25237     {
25238         
25239         this.assignDocWin();
25240         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25241         
25242         var range = this.createRange(sel);
25243          
25244         try {
25245             var p = range.commonAncestorContainer;
25246             while (p.nodeType == 3) { // text node
25247                 p = p.parentNode;
25248             }
25249             return p;
25250         } catch (e) {
25251             return null;
25252         }
25253     
25254     },
25255     /***
25256      *
25257      * Range intersection.. the hard stuff...
25258      *  '-1' = before
25259      *  '0' = hits..
25260      *  '1' = after.
25261      *         [ -- selected range --- ]
25262      *   [fail]                        [fail]
25263      *
25264      *    basically..
25265      *      if end is before start or  hits it. fail.
25266      *      if start is after end or hits it fail.
25267      *
25268      *   if either hits (but other is outside. - then it's not 
25269      *   
25270      *    
25271      **/
25272     
25273     
25274     // @see http://www.thismuchiknow.co.uk/?p=64.
25275     rangeIntersectsNode : function(range, node)
25276     {
25277         var nodeRange = node.ownerDocument.createRange();
25278         try {
25279             nodeRange.selectNode(node);
25280         } catch (e) {
25281             nodeRange.selectNodeContents(node);
25282         }
25283     
25284         var rangeStartRange = range.cloneRange();
25285         rangeStartRange.collapse(true);
25286     
25287         var rangeEndRange = range.cloneRange();
25288         rangeEndRange.collapse(false);
25289     
25290         var nodeStartRange = nodeRange.cloneRange();
25291         nodeStartRange.collapse(true);
25292     
25293         var nodeEndRange = nodeRange.cloneRange();
25294         nodeEndRange.collapse(false);
25295     
25296         return rangeStartRange.compareBoundaryPoints(
25297                  Range.START_TO_START, nodeEndRange) == -1 &&
25298                rangeEndRange.compareBoundaryPoints(
25299                  Range.START_TO_START, nodeStartRange) == 1;
25300         
25301          
25302     },
25303     rangeCompareNode : function(range, node)
25304     {
25305         var nodeRange = node.ownerDocument.createRange();
25306         try {
25307             nodeRange.selectNode(node);
25308         } catch (e) {
25309             nodeRange.selectNodeContents(node);
25310         }
25311         
25312         
25313         range.collapse(true);
25314     
25315         nodeRange.collapse(true);
25316      
25317         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25318         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25319          
25320         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25321         
25322         var nodeIsBefore   =  ss == 1;
25323         var nodeIsAfter    = ee == -1;
25324         
25325         if (nodeIsBefore && nodeIsAfter) {
25326             return 0; // outer
25327         }
25328         if (!nodeIsBefore && nodeIsAfter) {
25329             return 1; //right trailed.
25330         }
25331         
25332         if (nodeIsBefore && !nodeIsAfter) {
25333             return 2;  // left trailed.
25334         }
25335         // fully contined.
25336         return 3;
25337     },
25338
25339     // private? - in a new class?
25340     cleanUpPaste :  function()
25341     {
25342         // cleans up the whole document..
25343         Roo.log('cleanuppaste');
25344         
25345         this.cleanUpChildren(this.doc.body);
25346         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25347         if (clean != this.doc.body.innerHTML) {
25348             this.doc.body.innerHTML = clean;
25349         }
25350         
25351     },
25352     
25353     cleanWordChars : function(input) {// change the chars to hex code
25354         var he = Roo.HtmlEditorCore;
25355         
25356         var output = input;
25357         Roo.each(he.swapCodes, function(sw) { 
25358             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25359             
25360             output = output.replace(swapper, sw[1]);
25361         });
25362         
25363         return output;
25364     },
25365     
25366     
25367     cleanUpChildren : function (n)
25368     {
25369         if (!n.childNodes.length) {
25370             return;
25371         }
25372         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25373            this.cleanUpChild(n.childNodes[i]);
25374         }
25375     },
25376     
25377     
25378         
25379     
25380     cleanUpChild : function (node)
25381     {
25382         var ed = this;
25383         //console.log(node);
25384         if (node.nodeName == "#text") {
25385             // clean up silly Windows -- stuff?
25386             return; 
25387         }
25388         if (node.nodeName == "#comment") {
25389             node.parentNode.removeChild(node);
25390             // clean up silly Windows -- stuff?
25391             return; 
25392         }
25393         var lcname = node.tagName.toLowerCase();
25394         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25395         // whitelist of tags..
25396         
25397         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25398             // remove node.
25399             node.parentNode.removeChild(node);
25400             return;
25401             
25402         }
25403         
25404         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25405         
25406         // spans with no attributes - just remove them..
25407         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25408             remove_keep_children = true;
25409         }
25410         
25411         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25412         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25413         
25414         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25415         //    remove_keep_children = true;
25416         //}
25417         
25418         if (remove_keep_children) {
25419             this.cleanUpChildren(node);
25420             // inserts everything just before this node...
25421             while (node.childNodes.length) {
25422                 var cn = node.childNodes[0];
25423                 node.removeChild(cn);
25424                 node.parentNode.insertBefore(cn, node);
25425             }
25426             node.parentNode.removeChild(node);
25427             return;
25428         }
25429         
25430         if (!node.attributes || !node.attributes.length) {
25431             
25432           
25433             
25434             
25435             this.cleanUpChildren(node);
25436             return;
25437         }
25438         
25439         function cleanAttr(n,v)
25440         {
25441             
25442             if (v.match(/^\./) || v.match(/^\//)) {
25443                 return;
25444             }
25445             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25446                 return;
25447             }
25448             if (v.match(/^#/)) {
25449                 return;
25450             }
25451             if (v.match(/^\{/)) { // allow template editing.
25452                 return;
25453             }
25454 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25455             node.removeAttribute(n);
25456             
25457         }
25458         
25459         var cwhite = this.cwhite;
25460         var cblack = this.cblack;
25461             
25462         function cleanStyle(n,v)
25463         {
25464             if (v.match(/expression/)) { //XSS?? should we even bother..
25465                 node.removeAttribute(n);
25466                 return;
25467             }
25468             
25469             var parts = v.split(/;/);
25470             var clean = [];
25471             
25472             Roo.each(parts, function(p) {
25473                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25474                 if (!p.length) {
25475                     return true;
25476                 }
25477                 var l = p.split(':').shift().replace(/\s+/g,'');
25478                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25479                 
25480                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25481 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25482                     //node.removeAttribute(n);
25483                     return true;
25484                 }
25485                 //Roo.log()
25486                 // only allow 'c whitelisted system attributes'
25487                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25488 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25489                     //node.removeAttribute(n);
25490                     return true;
25491                 }
25492                 
25493                 
25494                  
25495                 
25496                 clean.push(p);
25497                 return true;
25498             });
25499             if (clean.length) { 
25500                 node.setAttribute(n, clean.join(';'));
25501             } else {
25502                 node.removeAttribute(n);
25503             }
25504             
25505         }
25506         
25507         
25508         for (var i = node.attributes.length-1; i > -1 ; i--) {
25509             var a = node.attributes[i];
25510             //console.log(a);
25511             
25512             if (a.name.toLowerCase().substr(0,2)=='on')  {
25513                 node.removeAttribute(a.name);
25514                 continue;
25515             }
25516             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25517                 node.removeAttribute(a.name);
25518                 continue;
25519             }
25520             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25521                 cleanAttr(a.name,a.value); // fixme..
25522                 continue;
25523             }
25524             if (a.name == 'style') {
25525                 cleanStyle(a.name,a.value);
25526                 continue;
25527             }
25528             /// clean up MS crap..
25529             // tecnically this should be a list of valid class'es..
25530             
25531             
25532             if (a.name == 'class') {
25533                 if (a.value.match(/^Mso/)) {
25534                     node.removeAttribute('class');
25535                 }
25536                 
25537                 if (a.value.match(/^body$/)) {
25538                     node.removeAttribute('class');
25539                 }
25540                 continue;
25541             }
25542             
25543             // style cleanup!?
25544             // class cleanup?
25545             
25546         }
25547         
25548         
25549         this.cleanUpChildren(node);
25550         
25551         
25552     },
25553     
25554     /**
25555      * Clean up MS wordisms...
25556      */
25557     cleanWord : function(node)
25558     {
25559         if (!node) {
25560             this.cleanWord(this.doc.body);
25561             return;
25562         }
25563         
25564         if(
25565                 node.nodeName == 'SPAN' &&
25566                 !node.hasAttributes() &&
25567                 node.childNodes.length == 1 &&
25568                 node.firstChild.nodeName == "#text"  
25569         ) {
25570             var textNode = node.firstChild;
25571             node.removeChild(textNode);
25572             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25573                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25574             }
25575             node.parentNode.insertBefore(textNode, node);
25576             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25577                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25578             }
25579             node.parentNode.removeChild(node);
25580         }
25581         
25582         if (node.nodeName == "#text") {
25583             // clean up silly Windows -- stuff?
25584             return; 
25585         }
25586         if (node.nodeName == "#comment") {
25587             node.parentNode.removeChild(node);
25588             // clean up silly Windows -- stuff?
25589             return; 
25590         }
25591         
25592         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25593             node.parentNode.removeChild(node);
25594             return;
25595         }
25596         //Roo.log(node.tagName);
25597         // remove - but keep children..
25598         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25599             //Roo.log('-- removed');
25600             while (node.childNodes.length) {
25601                 var cn = node.childNodes[0];
25602                 node.removeChild(cn);
25603                 node.parentNode.insertBefore(cn, node);
25604                 // move node to parent - and clean it..
25605                 this.cleanWord(cn);
25606             }
25607             node.parentNode.removeChild(node);
25608             /// no need to iterate chidlren = it's got none..
25609             //this.iterateChildren(node, this.cleanWord);
25610             return;
25611         }
25612         // clean styles
25613         if (node.className.length) {
25614             
25615             var cn = node.className.split(/\W+/);
25616             var cna = [];
25617             Roo.each(cn, function(cls) {
25618                 if (cls.match(/Mso[a-zA-Z]+/)) {
25619                     return;
25620                 }
25621                 cna.push(cls);
25622             });
25623             node.className = cna.length ? cna.join(' ') : '';
25624             if (!cna.length) {
25625                 node.removeAttribute("class");
25626             }
25627         }
25628         
25629         if (node.hasAttribute("lang")) {
25630             node.removeAttribute("lang");
25631         }
25632         
25633         if (node.hasAttribute("style")) {
25634             
25635             var styles = node.getAttribute("style").split(";");
25636             var nstyle = [];
25637             Roo.each(styles, function(s) {
25638                 if (!s.match(/:/)) {
25639                     return;
25640                 }
25641                 var kv = s.split(":");
25642                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25643                     return;
25644                 }
25645                 // what ever is left... we allow.
25646                 nstyle.push(s);
25647             });
25648             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25649             if (!nstyle.length) {
25650                 node.removeAttribute('style');
25651             }
25652         }
25653         this.iterateChildren(node, this.cleanWord);
25654         
25655         
25656         
25657     },
25658     /**
25659      * iterateChildren of a Node, calling fn each time, using this as the scole..
25660      * @param {DomNode} node node to iterate children of.
25661      * @param {Function} fn method of this class to call on each item.
25662      */
25663     iterateChildren : function(node, fn)
25664     {
25665         if (!node.childNodes.length) {
25666                 return;
25667         }
25668         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25669            fn.call(this, node.childNodes[i])
25670         }
25671     },
25672     
25673     
25674     /**
25675      * cleanTableWidths.
25676      *
25677      * Quite often pasting from word etc.. results in tables with column and widths.
25678      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25679      *
25680      */
25681     cleanTableWidths : function(node)
25682     {
25683          
25684          
25685         if (!node) {
25686             this.cleanTableWidths(this.doc.body);
25687             return;
25688         }
25689         
25690         // ignore list...
25691         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25692             return; 
25693         }
25694         Roo.log(node.tagName);
25695         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25696             this.iterateChildren(node, this.cleanTableWidths);
25697             return;
25698         }
25699         if (node.hasAttribute('width')) {
25700             node.removeAttribute('width');
25701         }
25702         
25703          
25704         if (node.hasAttribute("style")) {
25705             // pretty basic...
25706             
25707             var styles = node.getAttribute("style").split(";");
25708             var nstyle = [];
25709             Roo.each(styles, function(s) {
25710                 if (!s.match(/:/)) {
25711                     return;
25712                 }
25713                 var kv = s.split(":");
25714                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25715                     return;
25716                 }
25717                 // what ever is left... we allow.
25718                 nstyle.push(s);
25719             });
25720             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25721             if (!nstyle.length) {
25722                 node.removeAttribute('style');
25723             }
25724         }
25725         
25726         this.iterateChildren(node, this.cleanTableWidths);
25727         
25728         
25729     },
25730     
25731     
25732     
25733     
25734     domToHTML : function(currentElement, depth, nopadtext) {
25735         
25736         depth = depth || 0;
25737         nopadtext = nopadtext || false;
25738     
25739         if (!currentElement) {
25740             return this.domToHTML(this.doc.body);
25741         }
25742         
25743         //Roo.log(currentElement);
25744         var j;
25745         var allText = false;
25746         var nodeName = currentElement.nodeName;
25747         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25748         
25749         if  (nodeName == '#text') {
25750             
25751             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25752         }
25753         
25754         
25755         var ret = '';
25756         if (nodeName != 'BODY') {
25757              
25758             var i = 0;
25759             // Prints the node tagName, such as <A>, <IMG>, etc
25760             if (tagName) {
25761                 var attr = [];
25762                 for(i = 0; i < currentElement.attributes.length;i++) {
25763                     // quoting?
25764                     var aname = currentElement.attributes.item(i).name;
25765                     if (!currentElement.attributes.item(i).value.length) {
25766                         continue;
25767                     }
25768                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25769                 }
25770                 
25771                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25772             } 
25773             else {
25774                 
25775                 // eack
25776             }
25777         } else {
25778             tagName = false;
25779         }
25780         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25781             return ret;
25782         }
25783         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25784             nopadtext = true;
25785         }
25786         
25787         
25788         // Traverse the tree
25789         i = 0;
25790         var currentElementChild = currentElement.childNodes.item(i);
25791         var allText = true;
25792         var innerHTML  = '';
25793         lastnode = '';
25794         while (currentElementChild) {
25795             // Formatting code (indent the tree so it looks nice on the screen)
25796             var nopad = nopadtext;
25797             if (lastnode == 'SPAN') {
25798                 nopad  = true;
25799             }
25800             // text
25801             if  (currentElementChild.nodeName == '#text') {
25802                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25803                 toadd = nopadtext ? toadd : toadd.trim();
25804                 if (!nopad && toadd.length > 80) {
25805                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25806                 }
25807                 innerHTML  += toadd;
25808                 
25809                 i++;
25810                 currentElementChild = currentElement.childNodes.item(i);
25811                 lastNode = '';
25812                 continue;
25813             }
25814             allText = false;
25815             
25816             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25817                 
25818             // Recursively traverse the tree structure of the child node
25819             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25820             lastnode = currentElementChild.nodeName;
25821             i++;
25822             currentElementChild=currentElement.childNodes.item(i);
25823         }
25824         
25825         ret += innerHTML;
25826         
25827         if (!allText) {
25828                 // The remaining code is mostly for formatting the tree
25829             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25830         }
25831         
25832         
25833         if (tagName) {
25834             ret+= "</"+tagName+">";
25835         }
25836         return ret;
25837         
25838     },
25839         
25840     applyBlacklists : function()
25841     {
25842         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25843         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25844         
25845         this.white = [];
25846         this.black = [];
25847         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25848             if (b.indexOf(tag) > -1) {
25849                 return;
25850             }
25851             this.white.push(tag);
25852             
25853         }, this);
25854         
25855         Roo.each(w, function(tag) {
25856             if (b.indexOf(tag) > -1) {
25857                 return;
25858             }
25859             if (this.white.indexOf(tag) > -1) {
25860                 return;
25861             }
25862             this.white.push(tag);
25863             
25864         }, this);
25865         
25866         
25867         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25868             if (w.indexOf(tag) > -1) {
25869                 return;
25870             }
25871             this.black.push(tag);
25872             
25873         }, this);
25874         
25875         Roo.each(b, function(tag) {
25876             if (w.indexOf(tag) > -1) {
25877                 return;
25878             }
25879             if (this.black.indexOf(tag) > -1) {
25880                 return;
25881             }
25882             this.black.push(tag);
25883             
25884         }, this);
25885         
25886         
25887         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25888         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25889         
25890         this.cwhite = [];
25891         this.cblack = [];
25892         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25893             if (b.indexOf(tag) > -1) {
25894                 return;
25895             }
25896             this.cwhite.push(tag);
25897             
25898         }, this);
25899         
25900         Roo.each(w, function(tag) {
25901             if (b.indexOf(tag) > -1) {
25902                 return;
25903             }
25904             if (this.cwhite.indexOf(tag) > -1) {
25905                 return;
25906             }
25907             this.cwhite.push(tag);
25908             
25909         }, this);
25910         
25911         
25912         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25913             if (w.indexOf(tag) > -1) {
25914                 return;
25915             }
25916             this.cblack.push(tag);
25917             
25918         }, this);
25919         
25920         Roo.each(b, function(tag) {
25921             if (w.indexOf(tag) > -1) {
25922                 return;
25923             }
25924             if (this.cblack.indexOf(tag) > -1) {
25925                 return;
25926             }
25927             this.cblack.push(tag);
25928             
25929         }, this);
25930     },
25931     
25932     setStylesheets : function(stylesheets)
25933     {
25934         if(typeof(stylesheets) == 'string'){
25935             Roo.get(this.iframe.contentDocument.head).createChild({
25936                 tag : 'link',
25937                 rel : 'stylesheet',
25938                 type : 'text/css',
25939                 href : stylesheets
25940             });
25941             
25942             return;
25943         }
25944         var _this = this;
25945      
25946         Roo.each(stylesheets, function(s) {
25947             if(!s.length){
25948                 return;
25949             }
25950             
25951             Roo.get(_this.iframe.contentDocument.head).createChild({
25952                 tag : 'link',
25953                 rel : 'stylesheet',
25954                 type : 'text/css',
25955                 href : s
25956             });
25957         });
25958
25959         
25960     },
25961     
25962     removeStylesheets : function()
25963     {
25964         var _this = this;
25965         
25966         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25967             s.remove();
25968         });
25969     },
25970     
25971     setStyle : function(style)
25972     {
25973         Roo.get(this.iframe.contentDocument.head).createChild({
25974             tag : 'style',
25975             type : 'text/css',
25976             html : style
25977         });
25978
25979         return;
25980     }
25981     
25982     // hide stuff that is not compatible
25983     /**
25984      * @event blur
25985      * @hide
25986      */
25987     /**
25988      * @event change
25989      * @hide
25990      */
25991     /**
25992      * @event focus
25993      * @hide
25994      */
25995     /**
25996      * @event specialkey
25997      * @hide
25998      */
25999     /**
26000      * @cfg {String} fieldClass @hide
26001      */
26002     /**
26003      * @cfg {String} focusClass @hide
26004      */
26005     /**
26006      * @cfg {String} autoCreate @hide
26007      */
26008     /**
26009      * @cfg {String} inputType @hide
26010      */
26011     /**
26012      * @cfg {String} invalidClass @hide
26013      */
26014     /**
26015      * @cfg {String} invalidText @hide
26016      */
26017     /**
26018      * @cfg {String} msgFx @hide
26019      */
26020     /**
26021      * @cfg {String} validateOnBlur @hide
26022      */
26023 });
26024
26025 Roo.HtmlEditorCore.white = [
26026         'area', 'br', 'img', 'input', 'hr', 'wbr',
26027         
26028        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26029        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26030        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26031        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26032        'table',   'ul',         'xmp', 
26033        
26034        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26035       'thead',   'tr', 
26036      
26037       'dir', 'menu', 'ol', 'ul', 'dl',
26038        
26039       'embed',  'object'
26040 ];
26041
26042
26043 Roo.HtmlEditorCore.black = [
26044     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26045         'applet', // 
26046         'base',   'basefont', 'bgsound', 'blink',  'body', 
26047         'frame',  'frameset', 'head',    'html',   'ilayer', 
26048         'iframe', 'layer',  'link',     'meta',    'object',   
26049         'script', 'style' ,'title',  'xml' // clean later..
26050 ];
26051 Roo.HtmlEditorCore.clean = [
26052     'script', 'style', 'title', 'xml'
26053 ];
26054 Roo.HtmlEditorCore.remove = [
26055     'font'
26056 ];
26057 // attributes..
26058
26059 Roo.HtmlEditorCore.ablack = [
26060     'on'
26061 ];
26062     
26063 Roo.HtmlEditorCore.aclean = [ 
26064     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26065 ];
26066
26067 // protocols..
26068 Roo.HtmlEditorCore.pwhite= [
26069         'http',  'https',  'mailto'
26070 ];
26071
26072 // white listed style attributes.
26073 Roo.HtmlEditorCore.cwhite= [
26074       //  'text-align', /// default is to allow most things..
26075       
26076          
26077 //        'font-size'//??
26078 ];
26079
26080 // black listed style attributes.
26081 Roo.HtmlEditorCore.cblack= [
26082       //  'font-size' -- this can be set by the project 
26083 ];
26084
26085
26086 Roo.HtmlEditorCore.swapCodes   =[ 
26087     [    8211, "&#8211;" ], 
26088     [    8212, "&#8212;" ], 
26089     [    8216,  "'" ],  
26090     [    8217, "'" ],  
26091     [    8220, '"' ],  
26092     [    8221, '"' ],  
26093     [    8226, "*" ],  
26094     [    8230, "..." ]
26095 ]; 
26096
26097     /*
26098  * - LGPL
26099  *
26100  * HtmlEditor
26101  * 
26102  */
26103
26104 /**
26105  * @class Roo.bootstrap.HtmlEditor
26106  * @extends Roo.bootstrap.TextArea
26107  * Bootstrap HtmlEditor class
26108
26109  * @constructor
26110  * Create a new HtmlEditor
26111  * @param {Object} config The config object
26112  */
26113
26114 Roo.bootstrap.HtmlEditor = function(config){
26115     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26116     if (!this.toolbars) {
26117         this.toolbars = [];
26118     }
26119     
26120     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26121     this.addEvents({
26122             /**
26123              * @event initialize
26124              * Fires when the editor is fully initialized (including the iframe)
26125              * @param {HtmlEditor} this
26126              */
26127             initialize: true,
26128             /**
26129              * @event activate
26130              * Fires when the editor is first receives the focus. Any insertion must wait
26131              * until after this event.
26132              * @param {HtmlEditor} this
26133              */
26134             activate: true,
26135              /**
26136              * @event beforesync
26137              * Fires before the textarea is updated with content from the editor iframe. Return false
26138              * to cancel the sync.
26139              * @param {HtmlEditor} this
26140              * @param {String} html
26141              */
26142             beforesync: true,
26143              /**
26144              * @event beforepush
26145              * Fires before the iframe editor is updated with content from the textarea. Return false
26146              * to cancel the push.
26147              * @param {HtmlEditor} this
26148              * @param {String} html
26149              */
26150             beforepush: true,
26151              /**
26152              * @event sync
26153              * Fires when the textarea is updated with content from the editor iframe.
26154              * @param {HtmlEditor} this
26155              * @param {String} html
26156              */
26157             sync: true,
26158              /**
26159              * @event push
26160              * Fires when the iframe editor is updated with content from the textarea.
26161              * @param {HtmlEditor} this
26162              * @param {String} html
26163              */
26164             push: true,
26165              /**
26166              * @event editmodechange
26167              * Fires when the editor switches edit modes
26168              * @param {HtmlEditor} this
26169              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26170              */
26171             editmodechange: true,
26172             /**
26173              * @event editorevent
26174              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26175              * @param {HtmlEditor} this
26176              */
26177             editorevent: true,
26178             /**
26179              * @event firstfocus
26180              * Fires when on first focus - needed by toolbars..
26181              * @param {HtmlEditor} this
26182              */
26183             firstfocus: true,
26184             /**
26185              * @event autosave
26186              * Auto save the htmlEditor value as a file into Events
26187              * @param {HtmlEditor} this
26188              */
26189             autosave: true,
26190             /**
26191              * @event savedpreview
26192              * preview the saved version of htmlEditor
26193              * @param {HtmlEditor} this
26194              */
26195             savedpreview: true
26196         });
26197 };
26198
26199
26200 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26201     
26202     
26203       /**
26204      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26205      */
26206     toolbars : false,
26207     
26208      /**
26209     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26210     */
26211     btns : [],
26212    
26213      /**
26214      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26215      *                        Roo.resizable.
26216      */
26217     resizable : false,
26218      /**
26219      * @cfg {Number} height (in pixels)
26220      */   
26221     height: 300,
26222    /**
26223      * @cfg {Number} width (in pixels)
26224      */   
26225     width: false,
26226     
26227     /**
26228      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26229      * 
26230      */
26231     stylesheets: false,
26232     
26233     // id of frame..
26234     frameId: false,
26235     
26236     // private properties
26237     validationEvent : false,
26238     deferHeight: true,
26239     initialized : false,
26240     activated : false,
26241     
26242     onFocus : Roo.emptyFn,
26243     iframePad:3,
26244     hideMode:'offsets',
26245     
26246     tbContainer : false,
26247     
26248     bodyCls : '',
26249     
26250     toolbarContainer :function() {
26251         return this.wrap.select('.x-html-editor-tb',true).first();
26252     },
26253
26254     /**
26255      * Protected method that will not generally be called directly. It
26256      * is called when the editor creates its toolbar. Override this method if you need to
26257      * add custom toolbar buttons.
26258      * @param {HtmlEditor} editor
26259      */
26260     createToolbar : function(){
26261         Roo.log('renewing');
26262         Roo.log("create toolbars");
26263         
26264         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26265         this.toolbars[0].render(this.toolbarContainer());
26266         
26267         return;
26268         
26269 //        if (!editor.toolbars || !editor.toolbars.length) {
26270 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26271 //        }
26272 //        
26273 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26274 //            editor.toolbars[i] = Roo.factory(
26275 //                    typeof(editor.toolbars[i]) == 'string' ?
26276 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26277 //                Roo.bootstrap.HtmlEditor);
26278 //            editor.toolbars[i].init(editor);
26279 //        }
26280     },
26281
26282      
26283     // private
26284     onRender : function(ct, position)
26285     {
26286        // Roo.log("Call onRender: " + this.xtype);
26287         var _t = this;
26288         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26289       
26290         this.wrap = this.inputEl().wrap({
26291             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26292         });
26293         
26294         this.editorcore.onRender(ct, position);
26295          
26296         if (this.resizable) {
26297             this.resizeEl = new Roo.Resizable(this.wrap, {
26298                 pinned : true,
26299                 wrap: true,
26300                 dynamic : true,
26301                 minHeight : this.height,
26302                 height: this.height,
26303                 handles : this.resizable,
26304                 width: this.width,
26305                 listeners : {
26306                     resize : function(r, w, h) {
26307                         _t.onResize(w,h); // -something
26308                     }
26309                 }
26310             });
26311             
26312         }
26313         this.createToolbar(this);
26314        
26315         
26316         if(!this.width && this.resizable){
26317             this.setSize(this.wrap.getSize());
26318         }
26319         if (this.resizeEl) {
26320             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26321             // should trigger onReize..
26322         }
26323         
26324     },
26325
26326     // private
26327     onResize : function(w, h)
26328     {
26329         Roo.log('resize: ' +w + ',' + h );
26330         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26331         var ew = false;
26332         var eh = false;
26333         
26334         if(this.inputEl() ){
26335             if(typeof w == 'number'){
26336                 var aw = w - this.wrap.getFrameWidth('lr');
26337                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26338                 ew = aw;
26339             }
26340             if(typeof h == 'number'){
26341                  var tbh = -11;  // fixme it needs to tool bar size!
26342                 for (var i =0; i < this.toolbars.length;i++) {
26343                     // fixme - ask toolbars for heights?
26344                     tbh += this.toolbars[i].el.getHeight();
26345                     //if (this.toolbars[i].footer) {
26346                     //    tbh += this.toolbars[i].footer.el.getHeight();
26347                     //}
26348                 }
26349               
26350                 
26351                 
26352                 
26353                 
26354                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26355                 ah -= 5; // knock a few pixes off for look..
26356                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26357                 var eh = ah;
26358             }
26359         }
26360         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26361         this.editorcore.onResize(ew,eh);
26362         
26363     },
26364
26365     /**
26366      * Toggles the editor between standard and source edit mode.
26367      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26368      */
26369     toggleSourceEdit : function(sourceEditMode)
26370     {
26371         this.editorcore.toggleSourceEdit(sourceEditMode);
26372         
26373         if(this.editorcore.sourceEditMode){
26374             Roo.log('editor - showing textarea');
26375             
26376 //            Roo.log('in');
26377 //            Roo.log(this.syncValue());
26378             this.syncValue();
26379             this.inputEl().removeClass(['hide', 'x-hidden']);
26380             this.inputEl().dom.removeAttribute('tabIndex');
26381             this.inputEl().focus();
26382         }else{
26383             Roo.log('editor - hiding textarea');
26384 //            Roo.log('out')
26385 //            Roo.log(this.pushValue()); 
26386             this.pushValue();
26387             
26388             this.inputEl().addClass(['hide', 'x-hidden']);
26389             this.inputEl().dom.setAttribute('tabIndex', -1);
26390             //this.deferFocus();
26391         }
26392          
26393         if(this.resizable){
26394             this.setSize(this.wrap.getSize());
26395         }
26396         
26397         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26398     },
26399  
26400     // private (for BoxComponent)
26401     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26402
26403     // private (for BoxComponent)
26404     getResizeEl : function(){
26405         return this.wrap;
26406     },
26407
26408     // private (for BoxComponent)
26409     getPositionEl : function(){
26410         return this.wrap;
26411     },
26412
26413     // private
26414     initEvents : function(){
26415         this.originalValue = this.getValue();
26416     },
26417
26418 //    /**
26419 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26420 //     * @method
26421 //     */
26422 //    markInvalid : Roo.emptyFn,
26423 //    /**
26424 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26425 //     * @method
26426 //     */
26427 //    clearInvalid : Roo.emptyFn,
26428
26429     setValue : function(v){
26430         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26431         this.editorcore.pushValue();
26432     },
26433
26434      
26435     // private
26436     deferFocus : function(){
26437         this.focus.defer(10, this);
26438     },
26439
26440     // doc'ed in Field
26441     focus : function(){
26442         this.editorcore.focus();
26443         
26444     },
26445       
26446
26447     // private
26448     onDestroy : function(){
26449         
26450         
26451         
26452         if(this.rendered){
26453             
26454             for (var i =0; i < this.toolbars.length;i++) {
26455                 // fixme - ask toolbars for heights?
26456                 this.toolbars[i].onDestroy();
26457             }
26458             
26459             this.wrap.dom.innerHTML = '';
26460             this.wrap.remove();
26461         }
26462     },
26463
26464     // private
26465     onFirstFocus : function(){
26466         //Roo.log("onFirstFocus");
26467         this.editorcore.onFirstFocus();
26468          for (var i =0; i < this.toolbars.length;i++) {
26469             this.toolbars[i].onFirstFocus();
26470         }
26471         
26472     },
26473     
26474     // private
26475     syncValue : function()
26476     {   
26477         this.editorcore.syncValue();
26478     },
26479     
26480     pushValue : function()
26481     {   
26482         this.editorcore.pushValue();
26483     }
26484      
26485     
26486     // hide stuff that is not compatible
26487     /**
26488      * @event blur
26489      * @hide
26490      */
26491     /**
26492      * @event change
26493      * @hide
26494      */
26495     /**
26496      * @event focus
26497      * @hide
26498      */
26499     /**
26500      * @event specialkey
26501      * @hide
26502      */
26503     /**
26504      * @cfg {String} fieldClass @hide
26505      */
26506     /**
26507      * @cfg {String} focusClass @hide
26508      */
26509     /**
26510      * @cfg {String} autoCreate @hide
26511      */
26512     /**
26513      * @cfg {String} inputType @hide
26514      */
26515      
26516     /**
26517      * @cfg {String} invalidText @hide
26518      */
26519     /**
26520      * @cfg {String} msgFx @hide
26521      */
26522     /**
26523      * @cfg {String} validateOnBlur @hide
26524      */
26525 });
26526  
26527     
26528    
26529    
26530    
26531       
26532 Roo.namespace('Roo.bootstrap.htmleditor');
26533 /**
26534  * @class Roo.bootstrap.HtmlEditorToolbar1
26535  * Basic Toolbar
26536  * 
26537  * @example
26538  * Usage:
26539  *
26540  new Roo.bootstrap.HtmlEditor({
26541     ....
26542     toolbars : [
26543         new Roo.bootstrap.HtmlEditorToolbar1({
26544             disable : { fonts: 1 , format: 1, ..., ... , ...],
26545             btns : [ .... ]
26546         })
26547     }
26548      
26549  * 
26550  * @cfg {Object} disable List of elements to disable..
26551  * @cfg {Array} btns List of additional buttons.
26552  * 
26553  * 
26554  * NEEDS Extra CSS? 
26555  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26556  */
26557  
26558 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26559 {
26560     
26561     Roo.apply(this, config);
26562     
26563     // default disabled, based on 'good practice'..
26564     this.disable = this.disable || {};
26565     Roo.applyIf(this.disable, {
26566         fontSize : true,
26567         colors : true,
26568         specialElements : true
26569     });
26570     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26571     
26572     this.editor = config.editor;
26573     this.editorcore = config.editor.editorcore;
26574     
26575     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26576     
26577     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26578     // dont call parent... till later.
26579 }
26580 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26581      
26582     bar : true,
26583     
26584     editor : false,
26585     editorcore : false,
26586     
26587     
26588     formats : [
26589         "p" ,  
26590         "h1","h2","h3","h4","h5","h6", 
26591         "pre", "code", 
26592         "abbr", "acronym", "address", "cite", "samp", "var",
26593         'div','span'
26594     ],
26595     
26596     onRender : function(ct, position)
26597     {
26598        // Roo.log("Call onRender: " + this.xtype);
26599         
26600        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26601        Roo.log(this.el);
26602        this.el.dom.style.marginBottom = '0';
26603        var _this = this;
26604        var editorcore = this.editorcore;
26605        var editor= this.editor;
26606        
26607        var children = [];
26608        var btn = function(id,cmd , toggle, handler, html){
26609        
26610             var  event = toggle ? 'toggle' : 'click';
26611        
26612             var a = {
26613                 size : 'sm',
26614                 xtype: 'Button',
26615                 xns: Roo.bootstrap,
26616                 //glyphicon : id,
26617                 fa: id,
26618                 cmd : id || cmd,
26619                 enableToggle:toggle !== false,
26620                 html : html || '',
26621                 pressed : toggle ? false : null,
26622                 listeners : {}
26623             };
26624             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26625                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26626             };
26627             children.push(a);
26628             return a;
26629        }
26630        
26631     //    var cb_box = function...
26632         
26633         var style = {
26634                 xtype: 'Button',
26635                 size : 'sm',
26636                 xns: Roo.bootstrap,
26637                 fa : 'font',
26638                 //html : 'submit'
26639                 menu : {
26640                     xtype: 'Menu',
26641                     xns: Roo.bootstrap,
26642                     items:  []
26643                 }
26644         };
26645         Roo.each(this.formats, function(f) {
26646             style.menu.items.push({
26647                 xtype :'MenuItem',
26648                 xns: Roo.bootstrap,
26649                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26650                 tagname : f,
26651                 listeners : {
26652                     click : function()
26653                     {
26654                         editorcore.insertTag(this.tagname);
26655                         editor.focus();
26656                     }
26657                 }
26658                 
26659             });
26660         });
26661         children.push(style);   
26662         
26663         btn('bold',false,true);
26664         btn('italic',false,true);
26665         btn('align-left', 'justifyleft',true);
26666         btn('align-center', 'justifycenter',true);
26667         btn('align-right' , 'justifyright',true);
26668         btn('link', false, false, function(btn) {
26669             //Roo.log("create link?");
26670             var url = prompt(this.createLinkText, this.defaultLinkValue);
26671             if(url && url != 'http:/'+'/'){
26672                 this.editorcore.relayCmd('createlink', url);
26673             }
26674         }),
26675         btn('list','insertunorderedlist',true);
26676         btn('pencil', false,true, function(btn){
26677                 Roo.log(this);
26678                 this.toggleSourceEdit(btn.pressed);
26679         });
26680         
26681         if (this.editor.btns.length > 0) {
26682             for (var i = 0; i<this.editor.btns.length; i++) {
26683                 children.push(this.editor.btns[i]);
26684             }
26685         }
26686         
26687         /*
26688         var cog = {
26689                 xtype: 'Button',
26690                 size : 'sm',
26691                 xns: Roo.bootstrap,
26692                 glyphicon : 'cog',
26693                 //html : 'submit'
26694                 menu : {
26695                     xtype: 'Menu',
26696                     xns: Roo.bootstrap,
26697                     items:  []
26698                 }
26699         };
26700         
26701         cog.menu.items.push({
26702             xtype :'MenuItem',
26703             xns: Roo.bootstrap,
26704             html : Clean styles,
26705             tagname : f,
26706             listeners : {
26707                 click : function()
26708                 {
26709                     editorcore.insertTag(this.tagname);
26710                     editor.focus();
26711                 }
26712             }
26713             
26714         });
26715        */
26716         
26717          
26718        this.xtype = 'NavSimplebar';
26719         
26720         for(var i=0;i< children.length;i++) {
26721             
26722             this.buttons.add(this.addxtypeChild(children[i]));
26723             
26724         }
26725         
26726         editor.on('editorevent', this.updateToolbar, this);
26727     },
26728     onBtnClick : function(id)
26729     {
26730        this.editorcore.relayCmd(id);
26731        this.editorcore.focus();
26732     },
26733     
26734     /**
26735      * Protected method that will not generally be called directly. It triggers
26736      * a toolbar update by reading the markup state of the current selection in the editor.
26737      */
26738     updateToolbar: function(){
26739
26740         if(!this.editorcore.activated){
26741             this.editor.onFirstFocus(); // is this neeed?
26742             return;
26743         }
26744
26745         var btns = this.buttons; 
26746         var doc = this.editorcore.doc;
26747         btns.get('bold').setActive(doc.queryCommandState('bold'));
26748         btns.get('italic').setActive(doc.queryCommandState('italic'));
26749         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26750         
26751         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26752         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26753         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26754         
26755         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26756         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26757          /*
26758         
26759         var ans = this.editorcore.getAllAncestors();
26760         if (this.formatCombo) {
26761             
26762             
26763             var store = this.formatCombo.store;
26764             this.formatCombo.setValue("");
26765             for (var i =0; i < ans.length;i++) {
26766                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26767                     // select it..
26768                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26769                     break;
26770                 }
26771             }
26772         }
26773         
26774         
26775         
26776         // hides menus... - so this cant be on a menu...
26777         Roo.bootstrap.MenuMgr.hideAll();
26778         */
26779         Roo.bootstrap.MenuMgr.hideAll();
26780         //this.editorsyncValue();
26781     },
26782     onFirstFocus: function() {
26783         this.buttons.each(function(item){
26784            item.enable();
26785         });
26786     },
26787     toggleSourceEdit : function(sourceEditMode){
26788         
26789           
26790         if(sourceEditMode){
26791             Roo.log("disabling buttons");
26792            this.buttons.each( function(item){
26793                 if(item.cmd != 'pencil'){
26794                     item.disable();
26795                 }
26796             });
26797           
26798         }else{
26799             Roo.log("enabling buttons");
26800             if(this.editorcore.initialized){
26801                 this.buttons.each( function(item){
26802                     item.enable();
26803                 });
26804             }
26805             
26806         }
26807         Roo.log("calling toggole on editor");
26808         // tell the editor that it's been pressed..
26809         this.editor.toggleSourceEdit(sourceEditMode);
26810        
26811     }
26812 });
26813
26814
26815
26816
26817  
26818 /*
26819  * - LGPL
26820  */
26821
26822 /**
26823  * @class Roo.bootstrap.Markdown
26824  * @extends Roo.bootstrap.TextArea
26825  * Bootstrap Showdown editable area
26826  * @cfg {string} content
26827  * 
26828  * @constructor
26829  * Create a new Showdown
26830  */
26831
26832 Roo.bootstrap.Markdown = function(config){
26833     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26834    
26835 };
26836
26837 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26838     
26839     editing :false,
26840     
26841     initEvents : function()
26842     {
26843         
26844         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26845         this.markdownEl = this.el.createChild({
26846             cls : 'roo-markdown-area'
26847         });
26848         this.inputEl().addClass('d-none');
26849         if (this.getValue() == '') {
26850             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26851             
26852         } else {
26853             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26854         }
26855         this.markdownEl.on('click', this.toggleTextEdit, this);
26856         this.on('blur', this.toggleTextEdit, this);
26857         this.on('specialkey', this.resizeTextArea, this);
26858     },
26859     
26860     toggleTextEdit : function()
26861     {
26862         var sh = this.markdownEl.getHeight();
26863         this.inputEl().addClass('d-none');
26864         this.markdownEl.addClass('d-none');
26865         if (!this.editing) {
26866             // show editor?
26867             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26868             this.inputEl().removeClass('d-none');
26869             this.inputEl().focus();
26870             this.editing = true;
26871             return;
26872         }
26873         // show showdown...
26874         this.updateMarkdown();
26875         this.markdownEl.removeClass('d-none');
26876         this.editing = false;
26877         return;
26878     },
26879     updateMarkdown : function()
26880     {
26881         if (this.getValue() == '') {
26882             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26883             return;
26884         }
26885  
26886         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26887     },
26888     
26889     resizeTextArea: function () {
26890         
26891         var sh = 100;
26892         Roo.log([sh, this.getValue().split("\n").length * 30]);
26893         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26894     },
26895     setValue : function(val)
26896     {
26897         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26898         if (!this.editing) {
26899             this.updateMarkdown();
26900         }
26901         
26902     },
26903     focus : function()
26904     {
26905         if (!this.editing) {
26906             this.toggleTextEdit();
26907         }
26908         
26909     }
26910
26911
26912 });
26913 /**
26914  * @class Roo.bootstrap.Table.AbstractSelectionModel
26915  * @extends Roo.util.Observable
26916  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26917  * implemented by descendant classes.  This class should not be directly instantiated.
26918  * @constructor
26919  */
26920 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26921     this.locked = false;
26922     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26923 };
26924
26925
26926 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26927     /** @ignore Called by the grid automatically. Do not call directly. */
26928     init : function(grid){
26929         this.grid = grid;
26930         this.initEvents();
26931     },
26932
26933     /**
26934      * Locks the selections.
26935      */
26936     lock : function(){
26937         this.locked = true;
26938     },
26939
26940     /**
26941      * Unlocks the selections.
26942      */
26943     unlock : function(){
26944         this.locked = false;
26945     },
26946
26947     /**
26948      * Returns true if the selections are locked.
26949      * @return {Boolean}
26950      */
26951     isLocked : function(){
26952         return this.locked;
26953     },
26954     
26955     
26956     initEvents : function ()
26957     {
26958         
26959     }
26960 });
26961 /**
26962  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26963  * @class Roo.bootstrap.Table.RowSelectionModel
26964  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26965  * It supports multiple selections and keyboard selection/navigation. 
26966  * @constructor
26967  * @param {Object} config
26968  */
26969
26970 Roo.bootstrap.Table.RowSelectionModel = function(config){
26971     Roo.apply(this, config);
26972     this.selections = new Roo.util.MixedCollection(false, function(o){
26973         return o.id;
26974     });
26975
26976     this.last = false;
26977     this.lastActive = false;
26978
26979     this.addEvents({
26980         /**
26981              * @event selectionchange
26982              * Fires when the selection changes
26983              * @param {SelectionModel} this
26984              */
26985             "selectionchange" : true,
26986         /**
26987              * @event afterselectionchange
26988              * Fires after the selection changes (eg. by key press or clicking)
26989              * @param {SelectionModel} this
26990              */
26991             "afterselectionchange" : true,
26992         /**
26993              * @event beforerowselect
26994              * Fires when a row is selected being selected, return false to cancel.
26995              * @param {SelectionModel} this
26996              * @param {Number} rowIndex The selected index
26997              * @param {Boolean} keepExisting False if other selections will be cleared
26998              */
26999             "beforerowselect" : true,
27000         /**
27001              * @event rowselect
27002              * Fires when a row is selected.
27003              * @param {SelectionModel} this
27004              * @param {Number} rowIndex The selected index
27005              * @param {Roo.data.Record} r The record
27006              */
27007             "rowselect" : true,
27008         /**
27009              * @event rowdeselect
27010              * Fires when a row is deselected.
27011              * @param {SelectionModel} this
27012              * @param {Number} rowIndex The selected index
27013              */
27014         "rowdeselect" : true
27015     });
27016     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27017     this.locked = false;
27018  };
27019
27020 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27021     /**
27022      * @cfg {Boolean} singleSelect
27023      * True to allow selection of only one row at a time (defaults to false)
27024      */
27025     singleSelect : false,
27026
27027     // private
27028     initEvents : function()
27029     {
27030
27031         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27032         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27033         //}else{ // allow click to work like normal
27034          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27035         //}
27036         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27037         this.grid.on("rowclick", this.handleMouseDown, this);
27038         
27039         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27040             "up" : function(e){
27041                 if(!e.shiftKey){
27042                     this.selectPrevious(e.shiftKey);
27043                 }else if(this.last !== false && this.lastActive !== false){
27044                     var last = this.last;
27045                     this.selectRange(this.last,  this.lastActive-1);
27046                     this.grid.getView().focusRow(this.lastActive);
27047                     if(last !== false){
27048                         this.last = last;
27049                     }
27050                 }else{
27051                     this.selectFirstRow();
27052                 }
27053                 this.fireEvent("afterselectionchange", this);
27054             },
27055             "down" : function(e){
27056                 if(!e.shiftKey){
27057                     this.selectNext(e.shiftKey);
27058                 }else if(this.last !== false && this.lastActive !== false){
27059                     var last = this.last;
27060                     this.selectRange(this.last,  this.lastActive+1);
27061                     this.grid.getView().focusRow(this.lastActive);
27062                     if(last !== false){
27063                         this.last = last;
27064                     }
27065                 }else{
27066                     this.selectFirstRow();
27067                 }
27068                 this.fireEvent("afterselectionchange", this);
27069             },
27070             scope: this
27071         });
27072         this.grid.store.on('load', function(){
27073             this.selections.clear();
27074         },this);
27075         /*
27076         var view = this.grid.view;
27077         view.on("refresh", this.onRefresh, this);
27078         view.on("rowupdated", this.onRowUpdated, this);
27079         view.on("rowremoved", this.onRemove, this);
27080         */
27081     },
27082
27083     // private
27084     onRefresh : function()
27085     {
27086         var ds = this.grid.store, i, v = this.grid.view;
27087         var s = this.selections;
27088         s.each(function(r){
27089             if((i = ds.indexOfId(r.id)) != -1){
27090                 v.onRowSelect(i);
27091             }else{
27092                 s.remove(r);
27093             }
27094         });
27095     },
27096
27097     // private
27098     onRemove : function(v, index, r){
27099         this.selections.remove(r);
27100     },
27101
27102     // private
27103     onRowUpdated : function(v, index, r){
27104         if(this.isSelected(r)){
27105             v.onRowSelect(index);
27106         }
27107     },
27108
27109     /**
27110      * Select records.
27111      * @param {Array} records The records to select
27112      * @param {Boolean} keepExisting (optional) True to keep existing selections
27113      */
27114     selectRecords : function(records, keepExisting)
27115     {
27116         if(!keepExisting){
27117             this.clearSelections();
27118         }
27119             var ds = this.grid.store;
27120         for(var i = 0, len = records.length; i < len; i++){
27121             this.selectRow(ds.indexOf(records[i]), true);
27122         }
27123     },
27124
27125     /**
27126      * Gets the number of selected rows.
27127      * @return {Number}
27128      */
27129     getCount : function(){
27130         return this.selections.length;
27131     },
27132
27133     /**
27134      * Selects the first row in the grid.
27135      */
27136     selectFirstRow : function(){
27137         this.selectRow(0);
27138     },
27139
27140     /**
27141      * Select the last row.
27142      * @param {Boolean} keepExisting (optional) True to keep existing selections
27143      */
27144     selectLastRow : function(keepExisting){
27145         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27146         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27147     },
27148
27149     /**
27150      * Selects the row immediately following the last selected row.
27151      * @param {Boolean} keepExisting (optional) True to keep existing selections
27152      */
27153     selectNext : function(keepExisting)
27154     {
27155             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27156             this.selectRow(this.last+1, keepExisting);
27157             this.grid.getView().focusRow(this.last);
27158         }
27159     },
27160
27161     /**
27162      * Selects the row that precedes the last selected row.
27163      * @param {Boolean} keepExisting (optional) True to keep existing selections
27164      */
27165     selectPrevious : function(keepExisting){
27166         if(this.last){
27167             this.selectRow(this.last-1, keepExisting);
27168             this.grid.getView().focusRow(this.last);
27169         }
27170     },
27171
27172     /**
27173      * Returns the selected records
27174      * @return {Array} Array of selected records
27175      */
27176     getSelections : function(){
27177         return [].concat(this.selections.items);
27178     },
27179
27180     /**
27181      * Returns the first selected record.
27182      * @return {Record}
27183      */
27184     getSelected : function(){
27185         return this.selections.itemAt(0);
27186     },
27187
27188
27189     /**
27190      * Clears all selections.
27191      */
27192     clearSelections : function(fast)
27193     {
27194         if(this.locked) {
27195             return;
27196         }
27197         if(fast !== true){
27198                 var ds = this.grid.store;
27199             var s = this.selections;
27200             s.each(function(r){
27201                 this.deselectRow(ds.indexOfId(r.id));
27202             }, this);
27203             s.clear();
27204         }else{
27205             this.selections.clear();
27206         }
27207         this.last = false;
27208     },
27209
27210
27211     /**
27212      * Selects all rows.
27213      */
27214     selectAll : function(){
27215         if(this.locked) {
27216             return;
27217         }
27218         this.selections.clear();
27219         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27220             this.selectRow(i, true);
27221         }
27222     },
27223
27224     /**
27225      * Returns True if there is a selection.
27226      * @return {Boolean}
27227      */
27228     hasSelection : function(){
27229         return this.selections.length > 0;
27230     },
27231
27232     /**
27233      * Returns True if the specified row is selected.
27234      * @param {Number/Record} record The record or index of the record to check
27235      * @return {Boolean}
27236      */
27237     isSelected : function(index){
27238             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27239         return (r && this.selections.key(r.id) ? true : false);
27240     },
27241
27242     /**
27243      * Returns True if the specified record id is selected.
27244      * @param {String} id The id of record to check
27245      * @return {Boolean}
27246      */
27247     isIdSelected : function(id){
27248         return (this.selections.key(id) ? true : false);
27249     },
27250
27251
27252     // private
27253     handleMouseDBClick : function(e, t){
27254         
27255     },
27256     // private
27257     handleMouseDown : function(e, t)
27258     {
27259             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27260         if(this.isLocked() || rowIndex < 0 ){
27261             return;
27262         };
27263         if(e.shiftKey && this.last !== false){
27264             var last = this.last;
27265             this.selectRange(last, rowIndex, e.ctrlKey);
27266             this.last = last; // reset the last
27267             t.focus();
27268     
27269         }else{
27270             var isSelected = this.isSelected(rowIndex);
27271             //Roo.log("select row:" + rowIndex);
27272             if(isSelected){
27273                 this.deselectRow(rowIndex);
27274             } else {
27275                         this.selectRow(rowIndex, true);
27276             }
27277     
27278             /*
27279                 if(e.button !== 0 && isSelected){
27280                 alert('rowIndex 2: ' + rowIndex);
27281                     view.focusRow(rowIndex);
27282                 }else if(e.ctrlKey && isSelected){
27283                     this.deselectRow(rowIndex);
27284                 }else if(!isSelected){
27285                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27286                     view.focusRow(rowIndex);
27287                 }
27288             */
27289         }
27290         this.fireEvent("afterselectionchange", this);
27291     },
27292     // private
27293     handleDragableRowClick :  function(grid, rowIndex, e) 
27294     {
27295         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27296             this.selectRow(rowIndex, false);
27297             grid.view.focusRow(rowIndex);
27298              this.fireEvent("afterselectionchange", this);
27299         }
27300     },
27301     
27302     /**
27303      * Selects multiple rows.
27304      * @param {Array} rows Array of the indexes of the row to select
27305      * @param {Boolean} keepExisting (optional) True to keep existing selections
27306      */
27307     selectRows : function(rows, keepExisting){
27308         if(!keepExisting){
27309             this.clearSelections();
27310         }
27311         for(var i = 0, len = rows.length; i < len; i++){
27312             this.selectRow(rows[i], true);
27313         }
27314     },
27315
27316     /**
27317      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27318      * @param {Number} startRow The index of the first row in the range
27319      * @param {Number} endRow The index of the last row in the range
27320      * @param {Boolean} keepExisting (optional) True to retain existing selections
27321      */
27322     selectRange : function(startRow, endRow, keepExisting){
27323         if(this.locked) {
27324             return;
27325         }
27326         if(!keepExisting){
27327             this.clearSelections();
27328         }
27329         if(startRow <= endRow){
27330             for(var i = startRow; i <= endRow; i++){
27331                 this.selectRow(i, true);
27332             }
27333         }else{
27334             for(var i = startRow; i >= endRow; i--){
27335                 this.selectRow(i, true);
27336             }
27337         }
27338     },
27339
27340     /**
27341      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27342      * @param {Number} startRow The index of the first row in the range
27343      * @param {Number} endRow The index of the last row in the range
27344      */
27345     deselectRange : function(startRow, endRow, preventViewNotify){
27346         if(this.locked) {
27347             return;
27348         }
27349         for(var i = startRow; i <= endRow; i++){
27350             this.deselectRow(i, preventViewNotify);
27351         }
27352     },
27353
27354     /**
27355      * Selects a row.
27356      * @param {Number} row The index of the row to select
27357      * @param {Boolean} keepExisting (optional) True to keep existing selections
27358      */
27359     selectRow : function(index, keepExisting, preventViewNotify)
27360     {
27361             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27362             return;
27363         }
27364         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27365             if(!keepExisting || this.singleSelect){
27366                 this.clearSelections();
27367             }
27368             
27369             var r = this.grid.store.getAt(index);
27370             //console.log('selectRow - record id :' + r.id);
27371             
27372             this.selections.add(r);
27373             this.last = this.lastActive = index;
27374             if(!preventViewNotify){
27375                 var proxy = new Roo.Element(
27376                                 this.grid.getRowDom(index)
27377                 );
27378                 proxy.addClass('bg-info info');
27379             }
27380             this.fireEvent("rowselect", this, index, r);
27381             this.fireEvent("selectionchange", this);
27382         }
27383     },
27384
27385     /**
27386      * Deselects a row.
27387      * @param {Number} row The index of the row to deselect
27388      */
27389     deselectRow : function(index, preventViewNotify)
27390     {
27391         if(this.locked) {
27392             return;
27393         }
27394         if(this.last == index){
27395             this.last = false;
27396         }
27397         if(this.lastActive == index){
27398             this.lastActive = false;
27399         }
27400         
27401         var r = this.grid.store.getAt(index);
27402         if (!r) {
27403             return;
27404         }
27405         
27406         this.selections.remove(r);
27407         //.console.log('deselectRow - record id :' + r.id);
27408         if(!preventViewNotify){
27409         
27410             var proxy = new Roo.Element(
27411                 this.grid.getRowDom(index)
27412             );
27413             proxy.removeClass('bg-info info');
27414         }
27415         this.fireEvent("rowdeselect", this, index);
27416         this.fireEvent("selectionchange", this);
27417     },
27418
27419     // private
27420     restoreLast : function(){
27421         if(this._last){
27422             this.last = this._last;
27423         }
27424     },
27425
27426     // private
27427     acceptsNav : function(row, col, cm){
27428         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27429     },
27430
27431     // private
27432     onEditorKey : function(field, e){
27433         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27434         if(k == e.TAB){
27435             e.stopEvent();
27436             ed.completeEdit();
27437             if(e.shiftKey){
27438                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27439             }else{
27440                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27441             }
27442         }else if(k == e.ENTER && !e.ctrlKey){
27443             e.stopEvent();
27444             ed.completeEdit();
27445             if(e.shiftKey){
27446                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27447             }else{
27448                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27449             }
27450         }else if(k == e.ESC){
27451             ed.cancelEdit();
27452         }
27453         if(newCell){
27454             g.startEditing(newCell[0], newCell[1]);
27455         }
27456     }
27457 });
27458 /*
27459  * Based on:
27460  * Ext JS Library 1.1.1
27461  * Copyright(c) 2006-2007, Ext JS, LLC.
27462  *
27463  * Originally Released Under LGPL - original licence link has changed is not relivant.
27464  *
27465  * Fork - LGPL
27466  * <script type="text/javascript">
27467  */
27468  
27469 /**
27470  * @class Roo.bootstrap.PagingToolbar
27471  * @extends Roo.bootstrap.NavSimplebar
27472  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27473  * @constructor
27474  * Create a new PagingToolbar
27475  * @param {Object} config The config object
27476  * @param {Roo.data.Store} store
27477  */
27478 Roo.bootstrap.PagingToolbar = function(config)
27479 {
27480     // old args format still supported... - xtype is prefered..
27481         // created from xtype...
27482     
27483     this.ds = config.dataSource;
27484     
27485     if (config.store && !this.ds) {
27486         this.store= Roo.factory(config.store, Roo.data);
27487         this.ds = this.store;
27488         this.ds.xmodule = this.xmodule || false;
27489     }
27490     
27491     this.toolbarItems = [];
27492     if (config.items) {
27493         this.toolbarItems = config.items;
27494     }
27495     
27496     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27497     
27498     this.cursor = 0;
27499     
27500     if (this.ds) { 
27501         this.bind(this.ds);
27502     }
27503     
27504     if (Roo.bootstrap.version == 4) {
27505         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27506     } else {
27507         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27508     }
27509     
27510 };
27511
27512 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27513     /**
27514      * @cfg {Roo.data.Store} dataSource
27515      * The underlying data store providing the paged data
27516      */
27517     /**
27518      * @cfg {String/HTMLElement/Element} container
27519      * container The id or element that will contain the toolbar
27520      */
27521     /**
27522      * @cfg {Boolean} displayInfo
27523      * True to display the displayMsg (defaults to false)
27524      */
27525     /**
27526      * @cfg {Number} pageSize
27527      * The number of records to display per page (defaults to 20)
27528      */
27529     pageSize: 20,
27530     /**
27531      * @cfg {String} displayMsg
27532      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27533      */
27534     displayMsg : 'Displaying {0} - {1} of {2}',
27535     /**
27536      * @cfg {String} emptyMsg
27537      * The message to display when no records are found (defaults to "No data to display")
27538      */
27539     emptyMsg : 'No data to display',
27540     /**
27541      * Customizable piece of the default paging text (defaults to "Page")
27542      * @type String
27543      */
27544     beforePageText : "Page",
27545     /**
27546      * Customizable piece of the default paging text (defaults to "of %0")
27547      * @type String
27548      */
27549     afterPageText : "of {0}",
27550     /**
27551      * Customizable piece of the default paging text (defaults to "First Page")
27552      * @type String
27553      */
27554     firstText : "First Page",
27555     /**
27556      * Customizable piece of the default paging text (defaults to "Previous Page")
27557      * @type String
27558      */
27559     prevText : "Previous Page",
27560     /**
27561      * Customizable piece of the default paging text (defaults to "Next Page")
27562      * @type String
27563      */
27564     nextText : "Next Page",
27565     /**
27566      * Customizable piece of the default paging text (defaults to "Last Page")
27567      * @type String
27568      */
27569     lastText : "Last Page",
27570     /**
27571      * Customizable piece of the default paging text (defaults to "Refresh")
27572      * @type String
27573      */
27574     refreshText : "Refresh",
27575
27576     buttons : false,
27577     // private
27578     onRender : function(ct, position) 
27579     {
27580         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27581         this.navgroup.parentId = this.id;
27582         this.navgroup.onRender(this.el, null);
27583         // add the buttons to the navgroup
27584         
27585         if(this.displayInfo){
27586             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27587             this.displayEl = this.el.select('.x-paging-info', true).first();
27588 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27589 //            this.displayEl = navel.el.select('span',true).first();
27590         }
27591         
27592         var _this = this;
27593         
27594         if(this.buttons){
27595             Roo.each(_this.buttons, function(e){ // this might need to use render????
27596                Roo.factory(e).render(_this.el);
27597             });
27598         }
27599             
27600         Roo.each(_this.toolbarItems, function(e) {
27601             _this.navgroup.addItem(e);
27602         });
27603         
27604         
27605         this.first = this.navgroup.addItem({
27606             tooltip: this.firstText,
27607             cls: "prev btn-outline-secondary",
27608             html : ' <i class="fa fa-step-backward"></i>',
27609             disabled: true,
27610             preventDefault: true,
27611             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27612         });
27613         
27614         this.prev =  this.navgroup.addItem({
27615             tooltip: this.prevText,
27616             cls: "prev btn-outline-secondary",
27617             html : ' <i class="fa fa-backward"></i>',
27618             disabled: true,
27619             preventDefault: true,
27620             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27621         });
27622     //this.addSeparator();
27623         
27624         
27625         var field = this.navgroup.addItem( {
27626             tagtype : 'span',
27627             cls : 'x-paging-position  btn-outline-secondary',
27628              disabled: true,
27629             html : this.beforePageText  +
27630                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27631                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27632          } ); //?? escaped?
27633         
27634         this.field = field.el.select('input', true).first();
27635         this.field.on("keydown", this.onPagingKeydown, this);
27636         this.field.on("focus", function(){this.dom.select();});
27637     
27638     
27639         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27640         //this.field.setHeight(18);
27641         //this.addSeparator();
27642         this.next = this.navgroup.addItem({
27643             tooltip: this.nextText,
27644             cls: "next btn-outline-secondary",
27645             html : ' <i class="fa fa-forward"></i>',
27646             disabled: true,
27647             preventDefault: true,
27648             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27649         });
27650         this.last = this.navgroup.addItem({
27651             tooltip: this.lastText,
27652             html : ' <i class="fa fa-step-forward"></i>',
27653             cls: "next btn-outline-secondary",
27654             disabled: true,
27655             preventDefault: true,
27656             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27657         });
27658     //this.addSeparator();
27659         this.loading = this.navgroup.addItem({
27660             tooltip: this.refreshText,
27661             cls: "btn-outline-secondary",
27662             html : ' <i class="fa fa-refresh"></i>',
27663             preventDefault: true,
27664             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27665         });
27666         
27667     },
27668
27669     // private
27670     updateInfo : function(){
27671         if(this.displayEl){
27672             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27673             var msg = count == 0 ?
27674                 this.emptyMsg :
27675                 String.format(
27676                     this.displayMsg,
27677                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27678                 );
27679             this.displayEl.update(msg);
27680         }
27681     },
27682
27683     // private
27684     onLoad : function(ds, r, o)
27685     {
27686         this.cursor = o.params && o.params.start ? o.params.start : 0;
27687         
27688         var d = this.getPageData(),
27689             ap = d.activePage,
27690             ps = d.pages;
27691         
27692         
27693         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27694         this.field.dom.value = ap;
27695         this.first.setDisabled(ap == 1);
27696         this.prev.setDisabled(ap == 1);
27697         this.next.setDisabled(ap == ps);
27698         this.last.setDisabled(ap == ps);
27699         this.loading.enable();
27700         this.updateInfo();
27701     },
27702
27703     // private
27704     getPageData : function(){
27705         var total = this.ds.getTotalCount();
27706         return {
27707             total : total,
27708             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27709             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27710         };
27711     },
27712
27713     // private
27714     onLoadError : function(){
27715         this.loading.enable();
27716     },
27717
27718     // private
27719     onPagingKeydown : function(e){
27720         var k = e.getKey();
27721         var d = this.getPageData();
27722         if(k == e.RETURN){
27723             var v = this.field.dom.value, pageNum;
27724             if(!v || isNaN(pageNum = parseInt(v, 10))){
27725                 this.field.dom.value = d.activePage;
27726                 return;
27727             }
27728             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27729             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27730             e.stopEvent();
27731         }
27732         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))
27733         {
27734           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27735           this.field.dom.value = pageNum;
27736           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27737           e.stopEvent();
27738         }
27739         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27740         {
27741           var v = this.field.dom.value, pageNum; 
27742           var increment = (e.shiftKey) ? 10 : 1;
27743           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27744                 increment *= -1;
27745           }
27746           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27747             this.field.dom.value = d.activePage;
27748             return;
27749           }
27750           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27751           {
27752             this.field.dom.value = parseInt(v, 10) + increment;
27753             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27754             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27755           }
27756           e.stopEvent();
27757         }
27758     },
27759
27760     // private
27761     beforeLoad : function(){
27762         if(this.loading){
27763             this.loading.disable();
27764         }
27765     },
27766
27767     // private
27768     onClick : function(which){
27769         
27770         var ds = this.ds;
27771         if (!ds) {
27772             return;
27773         }
27774         
27775         switch(which){
27776             case "first":
27777                 ds.load({params:{start: 0, limit: this.pageSize}});
27778             break;
27779             case "prev":
27780                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27781             break;
27782             case "next":
27783                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27784             break;
27785             case "last":
27786                 var total = ds.getTotalCount();
27787                 var extra = total % this.pageSize;
27788                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27789                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27790             break;
27791             case "refresh":
27792                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27793             break;
27794         }
27795     },
27796
27797     /**
27798      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27799      * @param {Roo.data.Store} store The data store to unbind
27800      */
27801     unbind : function(ds){
27802         ds.un("beforeload", this.beforeLoad, this);
27803         ds.un("load", this.onLoad, this);
27804         ds.un("loadexception", this.onLoadError, this);
27805         ds.un("remove", this.updateInfo, this);
27806         ds.un("add", this.updateInfo, this);
27807         this.ds = undefined;
27808     },
27809
27810     /**
27811      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27812      * @param {Roo.data.Store} store The data store to bind
27813      */
27814     bind : function(ds){
27815         ds.on("beforeload", this.beforeLoad, this);
27816         ds.on("load", this.onLoad, this);
27817         ds.on("loadexception", this.onLoadError, this);
27818         ds.on("remove", this.updateInfo, this);
27819         ds.on("add", this.updateInfo, this);
27820         this.ds = ds;
27821     }
27822 });/*
27823  * - LGPL
27824  *
27825  * element
27826  * 
27827  */
27828
27829 /**
27830  * @class Roo.bootstrap.MessageBar
27831  * @extends Roo.bootstrap.Component
27832  * Bootstrap MessageBar class
27833  * @cfg {String} html contents of the MessageBar
27834  * @cfg {String} weight (info | success | warning | danger) default info
27835  * @cfg {String} beforeClass insert the bar before the given class
27836  * @cfg {Boolean} closable (true | false) default false
27837  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27838  * 
27839  * @constructor
27840  * Create a new Element
27841  * @param {Object} config The config object
27842  */
27843
27844 Roo.bootstrap.MessageBar = function(config){
27845     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27846 };
27847
27848 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27849     
27850     html: '',
27851     weight: 'info',
27852     closable: false,
27853     fixed: false,
27854     beforeClass: 'bootstrap-sticky-wrap',
27855     
27856     getAutoCreate : function(){
27857         
27858         var cfg = {
27859             tag: 'div',
27860             cls: 'alert alert-dismissable alert-' + this.weight,
27861             cn: [
27862                 {
27863                     tag: 'span',
27864                     cls: 'message',
27865                     html: this.html || ''
27866                 }
27867             ]
27868         };
27869         
27870         if(this.fixed){
27871             cfg.cls += ' alert-messages-fixed';
27872         }
27873         
27874         if(this.closable){
27875             cfg.cn.push({
27876                 tag: 'button',
27877                 cls: 'close',
27878                 html: 'x'
27879             });
27880         }
27881         
27882         return cfg;
27883     },
27884     
27885     onRender : function(ct, position)
27886     {
27887         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27888         
27889         if(!this.el){
27890             var cfg = Roo.apply({},  this.getAutoCreate());
27891             cfg.id = Roo.id();
27892             
27893             if (this.cls) {
27894                 cfg.cls += ' ' + this.cls;
27895             }
27896             if (this.style) {
27897                 cfg.style = this.style;
27898             }
27899             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27900             
27901             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27902         }
27903         
27904         this.el.select('>button.close').on('click', this.hide, this);
27905         
27906     },
27907     
27908     show : function()
27909     {
27910         if (!this.rendered) {
27911             this.render();
27912         }
27913         
27914         this.el.show();
27915         
27916         this.fireEvent('show', this);
27917         
27918     },
27919     
27920     hide : function()
27921     {
27922         if (!this.rendered) {
27923             this.render();
27924         }
27925         
27926         this.el.hide();
27927         
27928         this.fireEvent('hide', this);
27929     },
27930     
27931     update : function()
27932     {
27933 //        var e = this.el.dom.firstChild;
27934 //        
27935 //        if(this.closable){
27936 //            e = e.nextSibling;
27937 //        }
27938 //        
27939 //        e.data = this.html || '';
27940
27941         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27942     }
27943    
27944 });
27945
27946  
27947
27948      /*
27949  * - LGPL
27950  *
27951  * Graph
27952  * 
27953  */
27954
27955
27956 /**
27957  * @class Roo.bootstrap.Graph
27958  * @extends Roo.bootstrap.Component
27959  * Bootstrap Graph class
27960 > Prameters
27961  -sm {number} sm 4
27962  -md {number} md 5
27963  @cfg {String} graphtype  bar | vbar | pie
27964  @cfg {number} g_x coodinator | centre x (pie)
27965  @cfg {number} g_y coodinator | centre y (pie)
27966  @cfg {number} g_r radius (pie)
27967  @cfg {number} g_height height of the chart (respected by all elements in the set)
27968  @cfg {number} g_width width of the chart (respected by all elements in the set)
27969  @cfg {Object} title The title of the chart
27970     
27971  -{Array}  values
27972  -opts (object) options for the chart 
27973      o {
27974      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27975      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27976      o vgutter (number)
27977      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.
27978      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27979      o to
27980      o stretch (boolean)
27981      o }
27982  -opts (object) options for the pie
27983      o{
27984      o cut
27985      o startAngle (number)
27986      o endAngle (number)
27987      } 
27988  *
27989  * @constructor
27990  * Create a new Input
27991  * @param {Object} config The config object
27992  */
27993
27994 Roo.bootstrap.Graph = function(config){
27995     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27996     
27997     this.addEvents({
27998         // img events
27999         /**
28000          * @event click
28001          * The img click event for the img.
28002          * @param {Roo.EventObject} e
28003          */
28004         "click" : true
28005     });
28006 };
28007
28008 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28009     
28010     sm: 4,
28011     md: 5,
28012     graphtype: 'bar',
28013     g_height: 250,
28014     g_width: 400,
28015     g_x: 50,
28016     g_y: 50,
28017     g_r: 30,
28018     opts:{
28019         //g_colors: this.colors,
28020         g_type: 'soft',
28021         g_gutter: '20%'
28022
28023     },
28024     title : false,
28025
28026     getAutoCreate : function(){
28027         
28028         var cfg = {
28029             tag: 'div',
28030             html : null
28031         };
28032         
28033         
28034         return  cfg;
28035     },
28036
28037     onRender : function(ct,position){
28038         
28039         
28040         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28041         
28042         if (typeof(Raphael) == 'undefined') {
28043             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28044             return;
28045         }
28046         
28047         this.raphael = Raphael(this.el.dom);
28048         
28049                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28050                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28051                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28052                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28053                 /*
28054                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28055                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28056                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28057                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28058                 
28059                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28060                 r.barchart(330, 10, 300, 220, data1);
28061                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28062                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28063                 */
28064                 
28065                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28066                 // r.barchart(30, 30, 560, 250,  xdata, {
28067                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28068                 //     axis : "0 0 1 1",
28069                 //     axisxlabels :  xdata
28070                 //     //yvalues : cols,
28071                    
28072                 // });
28073 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28074 //        
28075 //        this.load(null,xdata,{
28076 //                axis : "0 0 1 1",
28077 //                axisxlabels :  xdata
28078 //                });
28079
28080     },
28081
28082     load : function(graphtype,xdata,opts)
28083     {
28084         this.raphael.clear();
28085         if(!graphtype) {
28086             graphtype = this.graphtype;
28087         }
28088         if(!opts){
28089             opts = this.opts;
28090         }
28091         var r = this.raphael,
28092             fin = function () {
28093                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28094             },
28095             fout = function () {
28096                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28097             },
28098             pfin = function() {
28099                 this.sector.stop();
28100                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28101
28102                 if (this.label) {
28103                     this.label[0].stop();
28104                     this.label[0].attr({ r: 7.5 });
28105                     this.label[1].attr({ "font-weight": 800 });
28106                 }
28107             },
28108             pfout = function() {
28109                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28110
28111                 if (this.label) {
28112                     this.label[0].animate({ r: 5 }, 500, "bounce");
28113                     this.label[1].attr({ "font-weight": 400 });
28114                 }
28115             };
28116
28117         switch(graphtype){
28118             case 'bar':
28119                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28120                 break;
28121             case 'hbar':
28122                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28123                 break;
28124             case 'pie':
28125 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28126 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28127 //            
28128                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28129                 
28130                 break;
28131
28132         }
28133         
28134         if(this.title){
28135             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28136         }
28137         
28138     },
28139     
28140     setTitle: function(o)
28141     {
28142         this.title = o;
28143     },
28144     
28145     initEvents: function() {
28146         
28147         if(!this.href){
28148             this.el.on('click', this.onClick, this);
28149         }
28150     },
28151     
28152     onClick : function(e)
28153     {
28154         Roo.log('img onclick');
28155         this.fireEvent('click', this, e);
28156     }
28157    
28158 });
28159
28160  
28161 /*
28162  * - LGPL
28163  *
28164  * numberBox
28165  * 
28166  */
28167 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28168
28169 /**
28170  * @class Roo.bootstrap.dash.NumberBox
28171  * @extends Roo.bootstrap.Component
28172  * Bootstrap NumberBox class
28173  * @cfg {String} headline Box headline
28174  * @cfg {String} content Box content
28175  * @cfg {String} icon Box icon
28176  * @cfg {String} footer Footer text
28177  * @cfg {String} fhref Footer href
28178  * 
28179  * @constructor
28180  * Create a new NumberBox
28181  * @param {Object} config The config object
28182  */
28183
28184
28185 Roo.bootstrap.dash.NumberBox = function(config){
28186     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28187     
28188 };
28189
28190 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28191     
28192     headline : '',
28193     content : '',
28194     icon : '',
28195     footer : '',
28196     fhref : '',
28197     ficon : '',
28198     
28199     getAutoCreate : function(){
28200         
28201         var cfg = {
28202             tag : 'div',
28203             cls : 'small-box ',
28204             cn : [
28205                 {
28206                     tag : 'div',
28207                     cls : 'inner',
28208                     cn :[
28209                         {
28210                             tag : 'h3',
28211                             cls : 'roo-headline',
28212                             html : this.headline
28213                         },
28214                         {
28215                             tag : 'p',
28216                             cls : 'roo-content',
28217                             html : this.content
28218                         }
28219                     ]
28220                 }
28221             ]
28222         };
28223         
28224         if(this.icon){
28225             cfg.cn.push({
28226                 tag : 'div',
28227                 cls : 'icon',
28228                 cn :[
28229                     {
28230                         tag : 'i',
28231                         cls : 'ion ' + this.icon
28232                     }
28233                 ]
28234             });
28235         }
28236         
28237         if(this.footer){
28238             var footer = {
28239                 tag : 'a',
28240                 cls : 'small-box-footer',
28241                 href : this.fhref || '#',
28242                 html : this.footer
28243             };
28244             
28245             cfg.cn.push(footer);
28246             
28247         }
28248         
28249         return  cfg;
28250     },
28251
28252     onRender : function(ct,position){
28253         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28254
28255
28256        
28257                 
28258     },
28259
28260     setHeadline: function (value)
28261     {
28262         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28263     },
28264     
28265     setFooter: function (value, href)
28266     {
28267         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28268         
28269         if(href){
28270             this.el.select('a.small-box-footer',true).first().attr('href', href);
28271         }
28272         
28273     },
28274
28275     setContent: function (value)
28276     {
28277         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28278     },
28279
28280     initEvents: function() 
28281     {   
28282         
28283     }
28284     
28285 });
28286
28287  
28288 /*
28289  * - LGPL
28290  *
28291  * TabBox
28292  * 
28293  */
28294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28295
28296 /**
28297  * @class Roo.bootstrap.dash.TabBox
28298  * @extends Roo.bootstrap.Component
28299  * Bootstrap TabBox class
28300  * @cfg {String} title Title of the TabBox
28301  * @cfg {String} icon Icon of the TabBox
28302  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28303  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28304  * 
28305  * @constructor
28306  * Create a new TabBox
28307  * @param {Object} config The config object
28308  */
28309
28310
28311 Roo.bootstrap.dash.TabBox = function(config){
28312     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28313     this.addEvents({
28314         // raw events
28315         /**
28316          * @event addpane
28317          * When a pane is added
28318          * @param {Roo.bootstrap.dash.TabPane} pane
28319          */
28320         "addpane" : true,
28321         /**
28322          * @event activatepane
28323          * When a pane is activated
28324          * @param {Roo.bootstrap.dash.TabPane} pane
28325          */
28326         "activatepane" : true
28327         
28328          
28329     });
28330     
28331     this.panes = [];
28332 };
28333
28334 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28335
28336     title : '',
28337     icon : false,
28338     showtabs : true,
28339     tabScrollable : false,
28340     
28341     getChildContainer : function()
28342     {
28343         return this.el.select('.tab-content', true).first();
28344     },
28345     
28346     getAutoCreate : function(){
28347         
28348         var header = {
28349             tag: 'li',
28350             cls: 'pull-left header',
28351             html: this.title,
28352             cn : []
28353         };
28354         
28355         if(this.icon){
28356             header.cn.push({
28357                 tag: 'i',
28358                 cls: 'fa ' + this.icon
28359             });
28360         }
28361         
28362         var h = {
28363             tag: 'ul',
28364             cls: 'nav nav-tabs pull-right',
28365             cn: [
28366                 header
28367             ]
28368         };
28369         
28370         if(this.tabScrollable){
28371             h = {
28372                 tag: 'div',
28373                 cls: 'tab-header',
28374                 cn: [
28375                     {
28376                         tag: 'ul',
28377                         cls: 'nav nav-tabs pull-right',
28378                         cn: [
28379                             header
28380                         ]
28381                     }
28382                 ]
28383             };
28384         }
28385         
28386         var cfg = {
28387             tag: 'div',
28388             cls: 'nav-tabs-custom',
28389             cn: [
28390                 h,
28391                 {
28392                     tag: 'div',
28393                     cls: 'tab-content no-padding',
28394                     cn: []
28395                 }
28396             ]
28397         };
28398
28399         return  cfg;
28400     },
28401     initEvents : function()
28402     {
28403         //Roo.log('add add pane handler');
28404         this.on('addpane', this.onAddPane, this);
28405     },
28406      /**
28407      * Updates the box title
28408      * @param {String} html to set the title to.
28409      */
28410     setTitle : function(value)
28411     {
28412         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28413     },
28414     onAddPane : function(pane)
28415     {
28416         this.panes.push(pane);
28417         //Roo.log('addpane');
28418         //Roo.log(pane);
28419         // tabs are rendere left to right..
28420         if(!this.showtabs){
28421             return;
28422         }
28423         
28424         var ctr = this.el.select('.nav-tabs', true).first();
28425          
28426          
28427         var existing = ctr.select('.nav-tab',true);
28428         var qty = existing.getCount();;
28429         
28430         
28431         var tab = ctr.createChild({
28432             tag : 'li',
28433             cls : 'nav-tab' + (qty ? '' : ' active'),
28434             cn : [
28435                 {
28436                     tag : 'a',
28437                     href:'#',
28438                     html : pane.title
28439                 }
28440             ]
28441         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28442         pane.tab = tab;
28443         
28444         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28445         if (!qty) {
28446             pane.el.addClass('active');
28447         }
28448         
28449                 
28450     },
28451     onTabClick : function(ev,un,ob,pane)
28452     {
28453         //Roo.log('tab - prev default');
28454         ev.preventDefault();
28455         
28456         
28457         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28458         pane.tab.addClass('active');
28459         //Roo.log(pane.title);
28460         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28461         // technically we should have a deactivate event.. but maybe add later.
28462         // and it should not de-activate the selected tab...
28463         this.fireEvent('activatepane', pane);
28464         pane.el.addClass('active');
28465         pane.fireEvent('activate');
28466         
28467         
28468     },
28469     
28470     getActivePane : function()
28471     {
28472         var r = false;
28473         Roo.each(this.panes, function(p) {
28474             if(p.el.hasClass('active')){
28475                 r = p;
28476                 return false;
28477             }
28478             
28479             return;
28480         });
28481         
28482         return r;
28483     }
28484     
28485     
28486 });
28487
28488  
28489 /*
28490  * - LGPL
28491  *
28492  * Tab pane
28493  * 
28494  */
28495 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28496 /**
28497  * @class Roo.bootstrap.TabPane
28498  * @extends Roo.bootstrap.Component
28499  * Bootstrap TabPane class
28500  * @cfg {Boolean} active (false | true) Default false
28501  * @cfg {String} title title of panel
28502
28503  * 
28504  * @constructor
28505  * Create a new TabPane
28506  * @param {Object} config The config object
28507  */
28508
28509 Roo.bootstrap.dash.TabPane = function(config){
28510     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28511     
28512     this.addEvents({
28513         // raw events
28514         /**
28515          * @event activate
28516          * When a pane is activated
28517          * @param {Roo.bootstrap.dash.TabPane} pane
28518          */
28519         "activate" : true
28520          
28521     });
28522 };
28523
28524 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28525     
28526     active : false,
28527     title : '',
28528     
28529     // the tabBox that this is attached to.
28530     tab : false,
28531      
28532     getAutoCreate : function() 
28533     {
28534         var cfg = {
28535             tag: 'div',
28536             cls: 'tab-pane'
28537         };
28538         
28539         if(this.active){
28540             cfg.cls += ' active';
28541         }
28542         
28543         return cfg;
28544     },
28545     initEvents  : function()
28546     {
28547         //Roo.log('trigger add pane handler');
28548         this.parent().fireEvent('addpane', this)
28549     },
28550     
28551      /**
28552      * Updates the tab title 
28553      * @param {String} html to set the title to.
28554      */
28555     setTitle: function(str)
28556     {
28557         if (!this.tab) {
28558             return;
28559         }
28560         this.title = str;
28561         this.tab.select('a', true).first().dom.innerHTML = str;
28562         
28563     }
28564     
28565     
28566     
28567 });
28568
28569  
28570
28571
28572  /*
28573  * - LGPL
28574  *
28575  * menu
28576  * 
28577  */
28578 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28579
28580 /**
28581  * @class Roo.bootstrap.menu.Menu
28582  * @extends Roo.bootstrap.Component
28583  * Bootstrap Menu class - container for Menu
28584  * @cfg {String} html Text of the menu
28585  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28586  * @cfg {String} icon Font awesome icon
28587  * @cfg {String} pos Menu align to (top | bottom) default bottom
28588  * 
28589  * 
28590  * @constructor
28591  * Create a new Menu
28592  * @param {Object} config The config object
28593  */
28594
28595
28596 Roo.bootstrap.menu.Menu = function(config){
28597     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28598     
28599     this.addEvents({
28600         /**
28601          * @event beforeshow
28602          * Fires before this menu is displayed
28603          * @param {Roo.bootstrap.menu.Menu} this
28604          */
28605         beforeshow : true,
28606         /**
28607          * @event beforehide
28608          * Fires before this menu is hidden
28609          * @param {Roo.bootstrap.menu.Menu} this
28610          */
28611         beforehide : true,
28612         /**
28613          * @event show
28614          * Fires after this menu is displayed
28615          * @param {Roo.bootstrap.menu.Menu} this
28616          */
28617         show : true,
28618         /**
28619          * @event hide
28620          * Fires after this menu is hidden
28621          * @param {Roo.bootstrap.menu.Menu} this
28622          */
28623         hide : true,
28624         /**
28625          * @event click
28626          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28627          * @param {Roo.bootstrap.menu.Menu} this
28628          * @param {Roo.EventObject} e
28629          */
28630         click : true
28631     });
28632     
28633 };
28634
28635 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28636     
28637     submenu : false,
28638     html : '',
28639     weight : 'default',
28640     icon : false,
28641     pos : 'bottom',
28642     
28643     
28644     getChildContainer : function() {
28645         if(this.isSubMenu){
28646             return this.el;
28647         }
28648         
28649         return this.el.select('ul.dropdown-menu', true).first();  
28650     },
28651     
28652     getAutoCreate : function()
28653     {
28654         var text = [
28655             {
28656                 tag : 'span',
28657                 cls : 'roo-menu-text',
28658                 html : this.html
28659             }
28660         ];
28661         
28662         if(this.icon){
28663             text.unshift({
28664                 tag : 'i',
28665                 cls : 'fa ' + this.icon
28666             })
28667         }
28668         
28669         
28670         var cfg = {
28671             tag : 'div',
28672             cls : 'btn-group',
28673             cn : [
28674                 {
28675                     tag : 'button',
28676                     cls : 'dropdown-button btn btn-' + this.weight,
28677                     cn : text
28678                 },
28679                 {
28680                     tag : 'button',
28681                     cls : 'dropdown-toggle btn btn-' + this.weight,
28682                     cn : [
28683                         {
28684                             tag : 'span',
28685                             cls : 'caret'
28686                         }
28687                     ]
28688                 },
28689                 {
28690                     tag : 'ul',
28691                     cls : 'dropdown-menu'
28692                 }
28693             ]
28694             
28695         };
28696         
28697         if(this.pos == 'top'){
28698             cfg.cls += ' dropup';
28699         }
28700         
28701         if(this.isSubMenu){
28702             cfg = {
28703                 tag : 'ul',
28704                 cls : 'dropdown-menu'
28705             }
28706         }
28707         
28708         return cfg;
28709     },
28710     
28711     onRender : function(ct, position)
28712     {
28713         this.isSubMenu = ct.hasClass('dropdown-submenu');
28714         
28715         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28716     },
28717     
28718     initEvents : function() 
28719     {
28720         if(this.isSubMenu){
28721             return;
28722         }
28723         
28724         this.hidden = true;
28725         
28726         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28727         this.triggerEl.on('click', this.onTriggerPress, this);
28728         
28729         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28730         this.buttonEl.on('click', this.onClick, this);
28731         
28732     },
28733     
28734     list : function()
28735     {
28736         if(this.isSubMenu){
28737             return this.el;
28738         }
28739         
28740         return this.el.select('ul.dropdown-menu', true).first();
28741     },
28742     
28743     onClick : function(e)
28744     {
28745         this.fireEvent("click", this, e);
28746     },
28747     
28748     onTriggerPress  : function(e)
28749     {   
28750         if (this.isVisible()) {
28751             this.hide();
28752         } else {
28753             this.show();
28754         }
28755     },
28756     
28757     isVisible : function(){
28758         return !this.hidden;
28759     },
28760     
28761     show : function()
28762     {
28763         this.fireEvent("beforeshow", this);
28764         
28765         this.hidden = false;
28766         this.el.addClass('open');
28767         
28768         Roo.get(document).on("mouseup", this.onMouseUp, this);
28769         
28770         this.fireEvent("show", this);
28771         
28772         
28773     },
28774     
28775     hide : function()
28776     {
28777         this.fireEvent("beforehide", this);
28778         
28779         this.hidden = true;
28780         this.el.removeClass('open');
28781         
28782         Roo.get(document).un("mouseup", this.onMouseUp);
28783         
28784         this.fireEvent("hide", this);
28785     },
28786     
28787     onMouseUp : function()
28788     {
28789         this.hide();
28790     }
28791     
28792 });
28793
28794  
28795  /*
28796  * - LGPL
28797  *
28798  * menu item
28799  * 
28800  */
28801 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28802
28803 /**
28804  * @class Roo.bootstrap.menu.Item
28805  * @extends Roo.bootstrap.Component
28806  * Bootstrap MenuItem class
28807  * @cfg {Boolean} submenu (true | false) default false
28808  * @cfg {String} html text of the item
28809  * @cfg {String} href the link
28810  * @cfg {Boolean} disable (true | false) default false
28811  * @cfg {Boolean} preventDefault (true | false) default true
28812  * @cfg {String} icon Font awesome icon
28813  * @cfg {String} pos Submenu align to (left | right) default right 
28814  * 
28815  * 
28816  * @constructor
28817  * Create a new Item
28818  * @param {Object} config The config object
28819  */
28820
28821
28822 Roo.bootstrap.menu.Item = function(config){
28823     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28824     this.addEvents({
28825         /**
28826          * @event mouseover
28827          * Fires when the mouse is hovering over this menu
28828          * @param {Roo.bootstrap.menu.Item} this
28829          * @param {Roo.EventObject} e
28830          */
28831         mouseover : true,
28832         /**
28833          * @event mouseout
28834          * Fires when the mouse exits this menu
28835          * @param {Roo.bootstrap.menu.Item} this
28836          * @param {Roo.EventObject} e
28837          */
28838         mouseout : true,
28839         // raw events
28840         /**
28841          * @event click
28842          * The raw click event for the entire grid.
28843          * @param {Roo.EventObject} e
28844          */
28845         click : true
28846     });
28847 };
28848
28849 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28850     
28851     submenu : false,
28852     href : '',
28853     html : '',
28854     preventDefault: true,
28855     disable : false,
28856     icon : false,
28857     pos : 'right',
28858     
28859     getAutoCreate : function()
28860     {
28861         var text = [
28862             {
28863                 tag : 'span',
28864                 cls : 'roo-menu-item-text',
28865                 html : this.html
28866             }
28867         ];
28868         
28869         if(this.icon){
28870             text.unshift({
28871                 tag : 'i',
28872                 cls : 'fa ' + this.icon
28873             })
28874         }
28875         
28876         var cfg = {
28877             tag : 'li',
28878             cn : [
28879                 {
28880                     tag : 'a',
28881                     href : this.href || '#',
28882                     cn : text
28883                 }
28884             ]
28885         };
28886         
28887         if(this.disable){
28888             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28889         }
28890         
28891         if(this.submenu){
28892             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28893             
28894             if(this.pos == 'left'){
28895                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28896             }
28897         }
28898         
28899         return cfg;
28900     },
28901     
28902     initEvents : function() 
28903     {
28904         this.el.on('mouseover', this.onMouseOver, this);
28905         this.el.on('mouseout', this.onMouseOut, this);
28906         
28907         this.el.select('a', true).first().on('click', this.onClick, this);
28908         
28909     },
28910     
28911     onClick : function(e)
28912     {
28913         if(this.preventDefault){
28914             e.preventDefault();
28915         }
28916         
28917         this.fireEvent("click", this, e);
28918     },
28919     
28920     onMouseOver : function(e)
28921     {
28922         if(this.submenu && this.pos == 'left'){
28923             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28924         }
28925         
28926         this.fireEvent("mouseover", this, e);
28927     },
28928     
28929     onMouseOut : function(e)
28930     {
28931         this.fireEvent("mouseout", this, e);
28932     }
28933 });
28934
28935  
28936
28937  /*
28938  * - LGPL
28939  *
28940  * menu separator
28941  * 
28942  */
28943 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28944
28945 /**
28946  * @class Roo.bootstrap.menu.Separator
28947  * @extends Roo.bootstrap.Component
28948  * Bootstrap Separator class
28949  * 
28950  * @constructor
28951  * Create a new Separator
28952  * @param {Object} config The config object
28953  */
28954
28955
28956 Roo.bootstrap.menu.Separator = function(config){
28957     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28958 };
28959
28960 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28961     
28962     getAutoCreate : function(){
28963         var cfg = {
28964             tag : 'li',
28965             cls: 'dropdown-divider divider'
28966         };
28967         
28968         return cfg;
28969     }
28970    
28971 });
28972
28973  
28974
28975  /*
28976  * - LGPL
28977  *
28978  * Tooltip
28979  * 
28980  */
28981
28982 /**
28983  * @class Roo.bootstrap.Tooltip
28984  * Bootstrap Tooltip class
28985  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28986  * to determine which dom element triggers the tooltip.
28987  * 
28988  * It needs to add support for additional attributes like tooltip-position
28989  * 
28990  * @constructor
28991  * Create a new Toolti
28992  * @param {Object} config The config object
28993  */
28994
28995 Roo.bootstrap.Tooltip = function(config){
28996     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28997     
28998     this.alignment = Roo.bootstrap.Tooltip.alignment;
28999     
29000     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29001         this.alignment = config.alignment;
29002     }
29003     
29004 };
29005
29006 Roo.apply(Roo.bootstrap.Tooltip, {
29007     /**
29008      * @function init initialize tooltip monitoring.
29009      * @static
29010      */
29011     currentEl : false,
29012     currentTip : false,
29013     currentRegion : false,
29014     
29015     //  init : delay?
29016     
29017     init : function()
29018     {
29019         Roo.get(document).on('mouseover', this.enter ,this);
29020         Roo.get(document).on('mouseout', this.leave, this);
29021          
29022         
29023         this.currentTip = new Roo.bootstrap.Tooltip();
29024     },
29025     
29026     enter : function(ev)
29027     {
29028         var dom = ev.getTarget();
29029         
29030         //Roo.log(['enter',dom]);
29031         var el = Roo.fly(dom);
29032         if (this.currentEl) {
29033             //Roo.log(dom);
29034             //Roo.log(this.currentEl);
29035             //Roo.log(this.currentEl.contains(dom));
29036             if (this.currentEl == el) {
29037                 return;
29038             }
29039             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29040                 return;
29041             }
29042
29043         }
29044         
29045         if (this.currentTip.el) {
29046             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29047         }    
29048         //Roo.log(ev);
29049         
29050         if(!el || el.dom == document){
29051             return;
29052         }
29053         
29054         var bindEl = el;
29055         
29056         // you can not look for children, as if el is the body.. then everythign is the child..
29057         if (!el.attr('tooltip')) { //
29058             if (!el.select("[tooltip]").elements.length) {
29059                 return;
29060             }
29061             // is the mouse over this child...?
29062             bindEl = el.select("[tooltip]").first();
29063             var xy = ev.getXY();
29064             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29065                 //Roo.log("not in region.");
29066                 return;
29067             }
29068             //Roo.log("child element over..");
29069             
29070         }
29071         this.currentEl = bindEl;
29072         this.currentTip.bind(bindEl);
29073         this.currentRegion = Roo.lib.Region.getRegion(dom);
29074         this.currentTip.enter();
29075         
29076     },
29077     leave : function(ev)
29078     {
29079         var dom = ev.getTarget();
29080         //Roo.log(['leave',dom]);
29081         if (!this.currentEl) {
29082             return;
29083         }
29084         
29085         
29086         if (dom != this.currentEl.dom) {
29087             return;
29088         }
29089         var xy = ev.getXY();
29090         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29091             return;
29092         }
29093         // only activate leave if mouse cursor is outside... bounding box..
29094         
29095         
29096         
29097         
29098         if (this.currentTip) {
29099             this.currentTip.leave();
29100         }
29101         //Roo.log('clear currentEl');
29102         this.currentEl = false;
29103         
29104         
29105     },
29106     alignment : {
29107         'left' : ['r-l', [-2,0], 'right'],
29108         'right' : ['l-r', [2,0], 'left'],
29109         'bottom' : ['t-b', [0,2], 'top'],
29110         'top' : [ 'b-t', [0,-2], 'bottom']
29111     }
29112     
29113 });
29114
29115
29116 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29117     
29118     
29119     bindEl : false,
29120     
29121     delay : null, // can be { show : 300 , hide: 500}
29122     
29123     timeout : null,
29124     
29125     hoverState : null, //???
29126     
29127     placement : 'bottom', 
29128     
29129     alignment : false,
29130     
29131     getAutoCreate : function(){
29132     
29133         var cfg = {
29134            cls : 'tooltip',   
29135            role : 'tooltip',
29136            cn : [
29137                 {
29138                     cls : 'tooltip-arrow arrow'
29139                 },
29140                 {
29141                     cls : 'tooltip-inner'
29142                 }
29143            ]
29144         };
29145         
29146         return cfg;
29147     },
29148     bind : function(el)
29149     {
29150         this.bindEl = el;
29151     },
29152     
29153     initEvents : function()
29154     {
29155         this.arrowEl = this.el.select('.arrow', true).first();
29156         this.innerEl = this.el.select('.tooltip-inner', true).first();
29157     },
29158     
29159     enter : function () {
29160        
29161         if (this.timeout != null) {
29162             clearTimeout(this.timeout);
29163         }
29164         
29165         this.hoverState = 'in';
29166          //Roo.log("enter - show");
29167         if (!this.delay || !this.delay.show) {
29168             this.show();
29169             return;
29170         }
29171         var _t = this;
29172         this.timeout = setTimeout(function () {
29173             if (_t.hoverState == 'in') {
29174                 _t.show();
29175             }
29176         }, this.delay.show);
29177     },
29178     leave : function()
29179     {
29180         clearTimeout(this.timeout);
29181     
29182         this.hoverState = 'out';
29183          if (!this.delay || !this.delay.hide) {
29184             this.hide();
29185             return;
29186         }
29187        
29188         var _t = this;
29189         this.timeout = setTimeout(function () {
29190             //Roo.log("leave - timeout");
29191             
29192             if (_t.hoverState == 'out') {
29193                 _t.hide();
29194                 Roo.bootstrap.Tooltip.currentEl = false;
29195             }
29196         }, delay);
29197     },
29198     
29199     show : function (msg)
29200     {
29201         if (!this.el) {
29202             this.render(document.body);
29203         }
29204         // set content.
29205         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29206         
29207         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29208         
29209         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29210         
29211         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29212                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29213         
29214         var placement = typeof this.placement == 'function' ?
29215             this.placement.call(this, this.el, on_el) :
29216             this.placement;
29217             
29218         var autoToken = /\s?auto?\s?/i;
29219         var autoPlace = autoToken.test(placement);
29220         if (autoPlace) {
29221             placement = placement.replace(autoToken, '') || 'top';
29222         }
29223         
29224         //this.el.detach()
29225         //this.el.setXY([0,0]);
29226         this.el.show();
29227         //this.el.dom.style.display='block';
29228         
29229         //this.el.appendTo(on_el);
29230         
29231         var p = this.getPosition();
29232         var box = this.el.getBox();
29233         
29234         if (autoPlace) {
29235             // fixme..
29236         }
29237         
29238         var align = this.alignment[placement];
29239         
29240         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29241         
29242         if(placement == 'top' || placement == 'bottom'){
29243             if(xy[0] < 0){
29244                 placement = 'right';
29245             }
29246             
29247             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29248                 placement = 'left';
29249             }
29250             
29251             var scroll = Roo.select('body', true).first().getScroll();
29252             
29253             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29254                 placement = 'top';
29255             }
29256             
29257             align = this.alignment[placement];
29258             
29259             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29260             
29261         }
29262         
29263         this.el.alignTo(this.bindEl, align[0],align[1]);
29264         //var arrow = this.el.select('.arrow',true).first();
29265         //arrow.set(align[2], 
29266         
29267         this.el.addClass(placement);
29268         this.el.addClass("bs-tooltip-"+ placement);
29269         
29270         this.el.addClass('in fade show');
29271         
29272         this.hoverState = null;
29273         
29274         if (this.el.hasClass('fade')) {
29275             // fade it?
29276         }
29277         
29278         
29279         
29280         
29281         
29282     },
29283     hide : function()
29284     {
29285          
29286         if (!this.el) {
29287             return;
29288         }
29289         //this.el.setXY([0,0]);
29290         this.el.removeClass(['show', 'in']);
29291         //this.el.hide();
29292         
29293     }
29294     
29295 });
29296  
29297
29298  /*
29299  * - LGPL
29300  *
29301  * Location Picker
29302  * 
29303  */
29304
29305 /**
29306  * @class Roo.bootstrap.LocationPicker
29307  * @extends Roo.bootstrap.Component
29308  * Bootstrap LocationPicker class
29309  * @cfg {Number} latitude Position when init default 0
29310  * @cfg {Number} longitude Position when init default 0
29311  * @cfg {Number} zoom default 15
29312  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29313  * @cfg {Boolean} mapTypeControl default false
29314  * @cfg {Boolean} disableDoubleClickZoom default false
29315  * @cfg {Boolean} scrollwheel default true
29316  * @cfg {Boolean} streetViewControl default false
29317  * @cfg {Number} radius default 0
29318  * @cfg {String} locationName
29319  * @cfg {Boolean} draggable default true
29320  * @cfg {Boolean} enableAutocomplete default false
29321  * @cfg {Boolean} enableReverseGeocode default true
29322  * @cfg {String} markerTitle
29323  * 
29324  * @constructor
29325  * Create a new LocationPicker
29326  * @param {Object} config The config object
29327  */
29328
29329
29330 Roo.bootstrap.LocationPicker = function(config){
29331     
29332     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29333     
29334     this.addEvents({
29335         /**
29336          * @event initial
29337          * Fires when the picker initialized.
29338          * @param {Roo.bootstrap.LocationPicker} this
29339          * @param {Google Location} location
29340          */
29341         initial : true,
29342         /**
29343          * @event positionchanged
29344          * Fires when the picker position changed.
29345          * @param {Roo.bootstrap.LocationPicker} this
29346          * @param {Google Location} location
29347          */
29348         positionchanged : true,
29349         /**
29350          * @event resize
29351          * Fires when the map resize.
29352          * @param {Roo.bootstrap.LocationPicker} this
29353          */
29354         resize : true,
29355         /**
29356          * @event show
29357          * Fires when the map show.
29358          * @param {Roo.bootstrap.LocationPicker} this
29359          */
29360         show : true,
29361         /**
29362          * @event hide
29363          * Fires when the map hide.
29364          * @param {Roo.bootstrap.LocationPicker} this
29365          */
29366         hide : true,
29367         /**
29368          * @event mapClick
29369          * Fires when click the map.
29370          * @param {Roo.bootstrap.LocationPicker} this
29371          * @param {Map event} e
29372          */
29373         mapClick : true,
29374         /**
29375          * @event mapRightClick
29376          * Fires when right click the map.
29377          * @param {Roo.bootstrap.LocationPicker} this
29378          * @param {Map event} e
29379          */
29380         mapRightClick : true,
29381         /**
29382          * @event markerClick
29383          * Fires when click the marker.
29384          * @param {Roo.bootstrap.LocationPicker} this
29385          * @param {Map event} e
29386          */
29387         markerClick : true,
29388         /**
29389          * @event markerRightClick
29390          * Fires when right click the marker.
29391          * @param {Roo.bootstrap.LocationPicker} this
29392          * @param {Map event} e
29393          */
29394         markerRightClick : true,
29395         /**
29396          * @event OverlayViewDraw
29397          * Fires when OverlayView Draw
29398          * @param {Roo.bootstrap.LocationPicker} this
29399          */
29400         OverlayViewDraw : true,
29401         /**
29402          * @event OverlayViewOnAdd
29403          * Fires when OverlayView Draw
29404          * @param {Roo.bootstrap.LocationPicker} this
29405          */
29406         OverlayViewOnAdd : true,
29407         /**
29408          * @event OverlayViewOnRemove
29409          * Fires when OverlayView Draw
29410          * @param {Roo.bootstrap.LocationPicker} this
29411          */
29412         OverlayViewOnRemove : true,
29413         /**
29414          * @event OverlayViewShow
29415          * Fires when OverlayView Draw
29416          * @param {Roo.bootstrap.LocationPicker} this
29417          * @param {Pixel} cpx
29418          */
29419         OverlayViewShow : true,
29420         /**
29421          * @event OverlayViewHide
29422          * Fires when OverlayView Draw
29423          * @param {Roo.bootstrap.LocationPicker} this
29424          */
29425         OverlayViewHide : true,
29426         /**
29427          * @event loadexception
29428          * Fires when load google lib failed.
29429          * @param {Roo.bootstrap.LocationPicker} this
29430          */
29431         loadexception : true
29432     });
29433         
29434 };
29435
29436 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29437     
29438     gMapContext: false,
29439     
29440     latitude: 0,
29441     longitude: 0,
29442     zoom: 15,
29443     mapTypeId: false,
29444     mapTypeControl: false,
29445     disableDoubleClickZoom: false,
29446     scrollwheel: true,
29447     streetViewControl: false,
29448     radius: 0,
29449     locationName: '',
29450     draggable: true,
29451     enableAutocomplete: false,
29452     enableReverseGeocode: true,
29453     markerTitle: '',
29454     
29455     getAutoCreate: function()
29456     {
29457
29458         var cfg = {
29459             tag: 'div',
29460             cls: 'roo-location-picker'
29461         };
29462         
29463         return cfg
29464     },
29465     
29466     initEvents: function(ct, position)
29467     {       
29468         if(!this.el.getWidth() || this.isApplied()){
29469             return;
29470         }
29471         
29472         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29473         
29474         this.initial();
29475     },
29476     
29477     initial: function()
29478     {
29479         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29480             this.fireEvent('loadexception', this);
29481             return;
29482         }
29483         
29484         if(!this.mapTypeId){
29485             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29486         }
29487         
29488         this.gMapContext = this.GMapContext();
29489         
29490         this.initOverlayView();
29491         
29492         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29493         
29494         var _this = this;
29495                 
29496         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29497             _this.setPosition(_this.gMapContext.marker.position);
29498         });
29499         
29500         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29501             _this.fireEvent('mapClick', this, event);
29502             
29503         });
29504
29505         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29506             _this.fireEvent('mapRightClick', this, event);
29507             
29508         });
29509         
29510         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29511             _this.fireEvent('markerClick', this, event);
29512             
29513         });
29514
29515         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29516             _this.fireEvent('markerRightClick', this, event);
29517             
29518         });
29519         
29520         this.setPosition(this.gMapContext.location);
29521         
29522         this.fireEvent('initial', this, this.gMapContext.location);
29523     },
29524     
29525     initOverlayView: function()
29526     {
29527         var _this = this;
29528         
29529         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29530             
29531             draw: function()
29532             {
29533                 _this.fireEvent('OverlayViewDraw', _this);
29534             },
29535             
29536             onAdd: function()
29537             {
29538                 _this.fireEvent('OverlayViewOnAdd', _this);
29539             },
29540             
29541             onRemove: function()
29542             {
29543                 _this.fireEvent('OverlayViewOnRemove', _this);
29544             },
29545             
29546             show: function(cpx)
29547             {
29548                 _this.fireEvent('OverlayViewShow', _this, cpx);
29549             },
29550             
29551             hide: function()
29552             {
29553                 _this.fireEvent('OverlayViewHide', _this);
29554             }
29555             
29556         });
29557     },
29558     
29559     fromLatLngToContainerPixel: function(event)
29560     {
29561         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29562     },
29563     
29564     isApplied: function() 
29565     {
29566         return this.getGmapContext() == false ? false : true;
29567     },
29568     
29569     getGmapContext: function() 
29570     {
29571         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29572     },
29573     
29574     GMapContext: function() 
29575     {
29576         var position = new google.maps.LatLng(this.latitude, this.longitude);
29577         
29578         var _map = new google.maps.Map(this.el.dom, {
29579             center: position,
29580             zoom: this.zoom,
29581             mapTypeId: this.mapTypeId,
29582             mapTypeControl: this.mapTypeControl,
29583             disableDoubleClickZoom: this.disableDoubleClickZoom,
29584             scrollwheel: this.scrollwheel,
29585             streetViewControl: this.streetViewControl,
29586             locationName: this.locationName,
29587             draggable: this.draggable,
29588             enableAutocomplete: this.enableAutocomplete,
29589             enableReverseGeocode: this.enableReverseGeocode
29590         });
29591         
29592         var _marker = new google.maps.Marker({
29593             position: position,
29594             map: _map,
29595             title: this.markerTitle,
29596             draggable: this.draggable
29597         });
29598         
29599         return {
29600             map: _map,
29601             marker: _marker,
29602             circle: null,
29603             location: position,
29604             radius: this.radius,
29605             locationName: this.locationName,
29606             addressComponents: {
29607                 formatted_address: null,
29608                 addressLine1: null,
29609                 addressLine2: null,
29610                 streetName: null,
29611                 streetNumber: null,
29612                 city: null,
29613                 district: null,
29614                 state: null,
29615                 stateOrProvince: null
29616             },
29617             settings: this,
29618             domContainer: this.el.dom,
29619             geodecoder: new google.maps.Geocoder()
29620         };
29621     },
29622     
29623     drawCircle: function(center, radius, options) 
29624     {
29625         if (this.gMapContext.circle != null) {
29626             this.gMapContext.circle.setMap(null);
29627         }
29628         if (radius > 0) {
29629             radius *= 1;
29630             options = Roo.apply({}, options, {
29631                 strokeColor: "#0000FF",
29632                 strokeOpacity: .35,
29633                 strokeWeight: 2,
29634                 fillColor: "#0000FF",
29635                 fillOpacity: .2
29636             });
29637             
29638             options.map = this.gMapContext.map;
29639             options.radius = radius;
29640             options.center = center;
29641             this.gMapContext.circle = new google.maps.Circle(options);
29642             return this.gMapContext.circle;
29643         }
29644         
29645         return null;
29646     },
29647     
29648     setPosition: function(location) 
29649     {
29650         this.gMapContext.location = location;
29651         this.gMapContext.marker.setPosition(location);
29652         this.gMapContext.map.panTo(location);
29653         this.drawCircle(location, this.gMapContext.radius, {});
29654         
29655         var _this = this;
29656         
29657         if (this.gMapContext.settings.enableReverseGeocode) {
29658             this.gMapContext.geodecoder.geocode({
29659                 latLng: this.gMapContext.location
29660             }, function(results, status) {
29661                 
29662                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29663                     _this.gMapContext.locationName = results[0].formatted_address;
29664                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29665                     
29666                     _this.fireEvent('positionchanged', this, location);
29667                 }
29668             });
29669             
29670             return;
29671         }
29672         
29673         this.fireEvent('positionchanged', this, location);
29674     },
29675     
29676     resize: function()
29677     {
29678         google.maps.event.trigger(this.gMapContext.map, "resize");
29679         
29680         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29681         
29682         this.fireEvent('resize', this);
29683     },
29684     
29685     setPositionByLatLng: function(latitude, longitude)
29686     {
29687         this.setPosition(new google.maps.LatLng(latitude, longitude));
29688     },
29689     
29690     getCurrentPosition: function() 
29691     {
29692         return {
29693             latitude: this.gMapContext.location.lat(),
29694             longitude: this.gMapContext.location.lng()
29695         };
29696     },
29697     
29698     getAddressName: function() 
29699     {
29700         return this.gMapContext.locationName;
29701     },
29702     
29703     getAddressComponents: function() 
29704     {
29705         return this.gMapContext.addressComponents;
29706     },
29707     
29708     address_component_from_google_geocode: function(address_components) 
29709     {
29710         var result = {};
29711         
29712         for (var i = 0; i < address_components.length; i++) {
29713             var component = address_components[i];
29714             if (component.types.indexOf("postal_code") >= 0) {
29715                 result.postalCode = component.short_name;
29716             } else if (component.types.indexOf("street_number") >= 0) {
29717                 result.streetNumber = component.short_name;
29718             } else if (component.types.indexOf("route") >= 0) {
29719                 result.streetName = component.short_name;
29720             } else if (component.types.indexOf("neighborhood") >= 0) {
29721                 result.city = component.short_name;
29722             } else if (component.types.indexOf("locality") >= 0) {
29723                 result.city = component.short_name;
29724             } else if (component.types.indexOf("sublocality") >= 0) {
29725                 result.district = component.short_name;
29726             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29727                 result.stateOrProvince = component.short_name;
29728             } else if (component.types.indexOf("country") >= 0) {
29729                 result.country = component.short_name;
29730             }
29731         }
29732         
29733         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29734         result.addressLine2 = "";
29735         return result;
29736     },
29737     
29738     setZoomLevel: function(zoom)
29739     {
29740         this.gMapContext.map.setZoom(zoom);
29741     },
29742     
29743     show: function()
29744     {
29745         if(!this.el){
29746             return;
29747         }
29748         
29749         this.el.show();
29750         
29751         this.resize();
29752         
29753         this.fireEvent('show', this);
29754     },
29755     
29756     hide: function()
29757     {
29758         if(!this.el){
29759             return;
29760         }
29761         
29762         this.el.hide();
29763         
29764         this.fireEvent('hide', this);
29765     }
29766     
29767 });
29768
29769 Roo.apply(Roo.bootstrap.LocationPicker, {
29770     
29771     OverlayView : function(map, options)
29772     {
29773         options = options || {};
29774         
29775         this.setMap(map);
29776     }
29777     
29778     
29779 });/**
29780  * @class Roo.bootstrap.Alert
29781  * @extends Roo.bootstrap.Component
29782  * Bootstrap Alert class - shows an alert area box
29783  * eg
29784  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29785   Enter a valid email address
29786 </div>
29787  * @licence LGPL
29788  * @cfg {String} title The title of alert
29789  * @cfg {String} html The content of alert
29790  * @cfg {String} weight (  success | info | warning | danger )
29791  * @cfg {String} faicon font-awesomeicon
29792  * 
29793  * @constructor
29794  * Create a new alert
29795  * @param {Object} config The config object
29796  */
29797
29798
29799 Roo.bootstrap.Alert = function(config){
29800     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29801     
29802 };
29803
29804 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29805     
29806     title: '',
29807     html: '',
29808     weight: false,
29809     faicon: false,
29810     
29811     getAutoCreate : function()
29812     {
29813         
29814         var cfg = {
29815             tag : 'div',
29816             cls : 'alert',
29817             cn : [
29818                 {
29819                     tag : 'i',
29820                     cls : 'roo-alert-icon'
29821                     
29822                 },
29823                 {
29824                     tag : 'b',
29825                     cls : 'roo-alert-title',
29826                     html : this.title
29827                 },
29828                 {
29829                     tag : 'span',
29830                     cls : 'roo-alert-text',
29831                     html : this.html
29832                 }
29833             ]
29834         };
29835         
29836         if(this.faicon){
29837             cfg.cn[0].cls += ' fa ' + this.faicon;
29838         }
29839         
29840         if(this.weight){
29841             cfg.cls += ' alert-' + this.weight;
29842         }
29843         
29844         return cfg;
29845     },
29846     
29847     initEvents: function() 
29848     {
29849         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29850     },
29851     
29852     setTitle : function(str)
29853     {
29854         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29855     },
29856     
29857     setText : function(str)
29858     {
29859         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29860     },
29861     
29862     setWeight : function(weight)
29863     {
29864         if(this.weight){
29865             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29866         }
29867         
29868         this.weight = weight;
29869         
29870         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29871     },
29872     
29873     setIcon : function(icon)
29874     {
29875         if(this.faicon){
29876             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29877         }
29878         
29879         this.faicon = icon;
29880         
29881         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29882     },
29883     
29884     hide: function() 
29885     {
29886         this.el.hide();   
29887     },
29888     
29889     show: function() 
29890     {  
29891         this.el.show();   
29892     }
29893     
29894 });
29895
29896  
29897 /*
29898 * Licence: LGPL
29899 */
29900
29901 /**
29902  * @class Roo.bootstrap.UploadCropbox
29903  * @extends Roo.bootstrap.Component
29904  * Bootstrap UploadCropbox class
29905  * @cfg {String} emptyText show when image has been loaded
29906  * @cfg {String} rotateNotify show when image too small to rotate
29907  * @cfg {Number} errorTimeout default 3000
29908  * @cfg {Number} minWidth default 300
29909  * @cfg {Number} minHeight default 300
29910  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29911  * @cfg {Boolean} isDocument (true|false) default false
29912  * @cfg {String} url action url
29913  * @cfg {String} paramName default 'imageUpload'
29914  * @cfg {String} method default POST
29915  * @cfg {Boolean} loadMask (true|false) default true
29916  * @cfg {Boolean} loadingText default 'Loading...'
29917  * 
29918  * @constructor
29919  * Create a new UploadCropbox
29920  * @param {Object} config The config object
29921  */
29922
29923 Roo.bootstrap.UploadCropbox = function(config){
29924     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29925     
29926     this.addEvents({
29927         /**
29928          * @event beforeselectfile
29929          * Fire before select file
29930          * @param {Roo.bootstrap.UploadCropbox} this
29931          */
29932         "beforeselectfile" : true,
29933         /**
29934          * @event initial
29935          * Fire after initEvent
29936          * @param {Roo.bootstrap.UploadCropbox} this
29937          */
29938         "initial" : true,
29939         /**
29940          * @event crop
29941          * Fire after initEvent
29942          * @param {Roo.bootstrap.UploadCropbox} this
29943          * @param {String} data
29944          */
29945         "crop" : true,
29946         /**
29947          * @event prepare
29948          * Fire when preparing the file data
29949          * @param {Roo.bootstrap.UploadCropbox} this
29950          * @param {Object} file
29951          */
29952         "prepare" : true,
29953         /**
29954          * @event exception
29955          * Fire when get exception
29956          * @param {Roo.bootstrap.UploadCropbox} this
29957          * @param {XMLHttpRequest} xhr
29958          */
29959         "exception" : true,
29960         /**
29961          * @event beforeloadcanvas
29962          * Fire before load the canvas
29963          * @param {Roo.bootstrap.UploadCropbox} this
29964          * @param {String} src
29965          */
29966         "beforeloadcanvas" : true,
29967         /**
29968          * @event trash
29969          * Fire when trash image
29970          * @param {Roo.bootstrap.UploadCropbox} this
29971          */
29972         "trash" : true,
29973         /**
29974          * @event download
29975          * Fire when download the image
29976          * @param {Roo.bootstrap.UploadCropbox} this
29977          */
29978         "download" : true,
29979         /**
29980          * @event footerbuttonclick
29981          * Fire when footerbuttonclick
29982          * @param {Roo.bootstrap.UploadCropbox} this
29983          * @param {String} type
29984          */
29985         "footerbuttonclick" : true,
29986         /**
29987          * @event resize
29988          * Fire when resize
29989          * @param {Roo.bootstrap.UploadCropbox} this
29990          */
29991         "resize" : true,
29992         /**
29993          * @event rotate
29994          * Fire when rotate the image
29995          * @param {Roo.bootstrap.UploadCropbox} this
29996          * @param {String} pos
29997          */
29998         "rotate" : true,
29999         /**
30000          * @event inspect
30001          * Fire when inspect the file
30002          * @param {Roo.bootstrap.UploadCropbox} this
30003          * @param {Object} file
30004          */
30005         "inspect" : true,
30006         /**
30007          * @event upload
30008          * Fire when xhr upload the file
30009          * @param {Roo.bootstrap.UploadCropbox} this
30010          * @param {Object} data
30011          */
30012         "upload" : true,
30013         /**
30014          * @event arrange
30015          * Fire when arrange the file data
30016          * @param {Roo.bootstrap.UploadCropbox} this
30017          * @param {Object} formData
30018          */
30019         "arrange" : true
30020     });
30021     
30022     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30023 };
30024
30025 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30026     
30027     emptyText : 'Click to upload image',
30028     rotateNotify : 'Image is too small to rotate',
30029     errorTimeout : 3000,
30030     scale : 0,
30031     baseScale : 1,
30032     rotate : 0,
30033     dragable : false,
30034     pinching : false,
30035     mouseX : 0,
30036     mouseY : 0,
30037     cropData : false,
30038     minWidth : 300,
30039     minHeight : 300,
30040     file : false,
30041     exif : {},
30042     baseRotate : 1,
30043     cropType : 'image/jpeg',
30044     buttons : false,
30045     canvasLoaded : false,
30046     isDocument : false,
30047     method : 'POST',
30048     paramName : 'imageUpload',
30049     loadMask : true,
30050     loadingText : 'Loading...',
30051     maskEl : false,
30052     
30053     getAutoCreate : function()
30054     {
30055         var cfg = {
30056             tag : 'div',
30057             cls : 'roo-upload-cropbox',
30058             cn : [
30059                 {
30060                     tag : 'input',
30061                     cls : 'roo-upload-cropbox-selector',
30062                     type : 'file'
30063                 },
30064                 {
30065                     tag : 'div',
30066                     cls : 'roo-upload-cropbox-body',
30067                     style : 'cursor:pointer',
30068                     cn : [
30069                         {
30070                             tag : 'div',
30071                             cls : 'roo-upload-cropbox-preview'
30072                         },
30073                         {
30074                             tag : 'div',
30075                             cls : 'roo-upload-cropbox-thumb'
30076                         },
30077                         {
30078                             tag : 'div',
30079                             cls : 'roo-upload-cropbox-empty-notify',
30080                             html : this.emptyText
30081                         },
30082                         {
30083                             tag : 'div',
30084                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30085                             html : this.rotateNotify
30086                         }
30087                     ]
30088                 },
30089                 {
30090                     tag : 'div',
30091                     cls : 'roo-upload-cropbox-footer',
30092                     cn : {
30093                         tag : 'div',
30094                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30095                         cn : []
30096                     }
30097                 }
30098             ]
30099         };
30100         
30101         return cfg;
30102     },
30103     
30104     onRender : function(ct, position)
30105     {
30106         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30107         
30108         if (this.buttons.length) {
30109             
30110             Roo.each(this.buttons, function(bb) {
30111                 
30112                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30113                 
30114                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30115                 
30116             }, this);
30117         }
30118         
30119         if(this.loadMask){
30120             this.maskEl = this.el;
30121         }
30122     },
30123     
30124     initEvents : function()
30125     {
30126         this.urlAPI = (window.createObjectURL && window) || 
30127                                 (window.URL && URL.revokeObjectURL && URL) || 
30128                                 (window.webkitURL && webkitURL);
30129                         
30130         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30131         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30132         
30133         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30134         this.selectorEl.hide();
30135         
30136         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30137         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30138         
30139         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30140         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30141         this.thumbEl.hide();
30142         
30143         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30144         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30145         
30146         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30147         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30148         this.errorEl.hide();
30149         
30150         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30151         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30152         this.footerEl.hide();
30153         
30154         this.setThumbBoxSize();
30155         
30156         this.bind();
30157         
30158         this.resize();
30159         
30160         this.fireEvent('initial', this);
30161     },
30162
30163     bind : function()
30164     {
30165         var _this = this;
30166         
30167         window.addEventListener("resize", function() { _this.resize(); } );
30168         
30169         this.bodyEl.on('click', this.beforeSelectFile, this);
30170         
30171         if(Roo.isTouch){
30172             this.bodyEl.on('touchstart', this.onTouchStart, this);
30173             this.bodyEl.on('touchmove', this.onTouchMove, this);
30174             this.bodyEl.on('touchend', this.onTouchEnd, this);
30175         }
30176         
30177         if(!Roo.isTouch){
30178             this.bodyEl.on('mousedown', this.onMouseDown, this);
30179             this.bodyEl.on('mousemove', this.onMouseMove, this);
30180             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30181             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30182             Roo.get(document).on('mouseup', this.onMouseUp, this);
30183         }
30184         
30185         this.selectorEl.on('change', this.onFileSelected, this);
30186     },
30187     
30188     reset : function()
30189     {    
30190         this.scale = 0;
30191         this.baseScale = 1;
30192         this.rotate = 0;
30193         this.baseRotate = 1;
30194         this.dragable = false;
30195         this.pinching = false;
30196         this.mouseX = 0;
30197         this.mouseY = 0;
30198         this.cropData = false;
30199         this.notifyEl.dom.innerHTML = this.emptyText;
30200         
30201         this.selectorEl.dom.value = '';
30202         
30203     },
30204     
30205     resize : function()
30206     {
30207         if(this.fireEvent('resize', this) != false){
30208             this.setThumbBoxPosition();
30209             this.setCanvasPosition();
30210         }
30211     },
30212     
30213     onFooterButtonClick : function(e, el, o, type)
30214     {
30215         switch (type) {
30216             case 'rotate-left' :
30217                 this.onRotateLeft(e);
30218                 break;
30219             case 'rotate-right' :
30220                 this.onRotateRight(e);
30221                 break;
30222             case 'picture' :
30223                 this.beforeSelectFile(e);
30224                 break;
30225             case 'trash' :
30226                 this.trash(e);
30227                 break;
30228             case 'crop' :
30229                 this.crop(e);
30230                 break;
30231             case 'download' :
30232                 this.download(e);
30233                 break;
30234             default :
30235                 break;
30236         }
30237         
30238         this.fireEvent('footerbuttonclick', this, type);
30239     },
30240     
30241     beforeSelectFile : function(e)
30242     {
30243         e.preventDefault();
30244         
30245         if(this.fireEvent('beforeselectfile', this) != false){
30246             this.selectorEl.dom.click();
30247         }
30248     },
30249     
30250     onFileSelected : function(e)
30251     {
30252         e.preventDefault();
30253         
30254         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30255             return;
30256         }
30257         
30258         var file = this.selectorEl.dom.files[0];
30259         
30260         if(this.fireEvent('inspect', this, file) != false){
30261             this.prepare(file);
30262         }
30263         
30264     },
30265     
30266     trash : function(e)
30267     {
30268         this.fireEvent('trash', this);
30269     },
30270     
30271     download : function(e)
30272     {
30273         this.fireEvent('download', this);
30274     },
30275     
30276     loadCanvas : function(src)
30277     {   
30278         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30279             
30280             this.reset();
30281             
30282             this.imageEl = document.createElement('img');
30283             
30284             var _this = this;
30285             
30286             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30287             
30288             this.imageEl.src = src;
30289         }
30290     },
30291     
30292     onLoadCanvas : function()
30293     {   
30294         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30295         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30296         
30297         this.bodyEl.un('click', this.beforeSelectFile, this);
30298         
30299         this.notifyEl.hide();
30300         this.thumbEl.show();
30301         this.footerEl.show();
30302         
30303         this.baseRotateLevel();
30304         
30305         if(this.isDocument){
30306             this.setThumbBoxSize();
30307         }
30308         
30309         this.setThumbBoxPosition();
30310         
30311         this.baseScaleLevel();
30312         
30313         this.draw();
30314         
30315         this.resize();
30316         
30317         this.canvasLoaded = true;
30318         
30319         if(this.loadMask){
30320             this.maskEl.unmask();
30321         }
30322         
30323     },
30324     
30325     setCanvasPosition : function()
30326     {   
30327         if(!this.canvasEl){
30328             return;
30329         }
30330         
30331         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30332         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30333         
30334         this.previewEl.setLeft(pw);
30335         this.previewEl.setTop(ph);
30336         
30337     },
30338     
30339     onMouseDown : function(e)
30340     {   
30341         e.stopEvent();
30342         
30343         this.dragable = true;
30344         this.pinching = false;
30345         
30346         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30347             this.dragable = false;
30348             return;
30349         }
30350         
30351         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30352         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30353         
30354     },
30355     
30356     onMouseMove : function(e)
30357     {   
30358         e.stopEvent();
30359         
30360         if(!this.canvasLoaded){
30361             return;
30362         }
30363         
30364         if (!this.dragable){
30365             return;
30366         }
30367         
30368         var minX = Math.ceil(this.thumbEl.getLeft(true));
30369         var minY = Math.ceil(this.thumbEl.getTop(true));
30370         
30371         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30372         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30373         
30374         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30375         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30376         
30377         x = x - this.mouseX;
30378         y = y - this.mouseY;
30379         
30380         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30381         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30382         
30383         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30384         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30385         
30386         this.previewEl.setLeft(bgX);
30387         this.previewEl.setTop(bgY);
30388         
30389         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30390         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30391     },
30392     
30393     onMouseUp : function(e)
30394     {   
30395         e.stopEvent();
30396         
30397         this.dragable = false;
30398     },
30399     
30400     onMouseWheel : function(e)
30401     {   
30402         e.stopEvent();
30403         
30404         this.startScale = this.scale;
30405         
30406         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30407         
30408         if(!this.zoomable()){
30409             this.scale = this.startScale;
30410             return;
30411         }
30412         
30413         this.draw();
30414         
30415         return;
30416     },
30417     
30418     zoomable : function()
30419     {
30420         var minScale = this.thumbEl.getWidth() / this.minWidth;
30421         
30422         if(this.minWidth < this.minHeight){
30423             minScale = this.thumbEl.getHeight() / this.minHeight;
30424         }
30425         
30426         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30427         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30428         
30429         if(
30430                 this.isDocument &&
30431                 (this.rotate == 0 || this.rotate == 180) && 
30432                 (
30433                     width > this.imageEl.OriginWidth || 
30434                     height > this.imageEl.OriginHeight ||
30435                     (width < this.minWidth && height < this.minHeight)
30436                 )
30437         ){
30438             return false;
30439         }
30440         
30441         if(
30442                 this.isDocument &&
30443                 (this.rotate == 90 || this.rotate == 270) && 
30444                 (
30445                     width > this.imageEl.OriginWidth || 
30446                     height > this.imageEl.OriginHeight ||
30447                     (width < this.minHeight && height < this.minWidth)
30448                 )
30449         ){
30450             return false;
30451         }
30452         
30453         if(
30454                 !this.isDocument &&
30455                 (this.rotate == 0 || this.rotate == 180) && 
30456                 (
30457                     width < this.minWidth || 
30458                     width > this.imageEl.OriginWidth || 
30459                     height < this.minHeight || 
30460                     height > this.imageEl.OriginHeight
30461                 )
30462         ){
30463             return false;
30464         }
30465         
30466         if(
30467                 !this.isDocument &&
30468                 (this.rotate == 90 || this.rotate == 270) && 
30469                 (
30470                     width < this.minHeight || 
30471                     width > this.imageEl.OriginWidth || 
30472                     height < this.minWidth || 
30473                     height > this.imageEl.OriginHeight
30474                 )
30475         ){
30476             return false;
30477         }
30478         
30479         return true;
30480         
30481     },
30482     
30483     onRotateLeft : function(e)
30484     {   
30485         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30486             
30487             var minScale = this.thumbEl.getWidth() / this.minWidth;
30488             
30489             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30490             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30491             
30492             this.startScale = this.scale;
30493             
30494             while (this.getScaleLevel() < minScale){
30495             
30496                 this.scale = this.scale + 1;
30497                 
30498                 if(!this.zoomable()){
30499                     break;
30500                 }
30501                 
30502                 if(
30503                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30504                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30505                 ){
30506                     continue;
30507                 }
30508                 
30509                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30510
30511                 this.draw();
30512                 
30513                 return;
30514             }
30515             
30516             this.scale = this.startScale;
30517             
30518             this.onRotateFail();
30519             
30520             return false;
30521         }
30522         
30523         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30524
30525         if(this.isDocument){
30526             this.setThumbBoxSize();
30527             this.setThumbBoxPosition();
30528             this.setCanvasPosition();
30529         }
30530         
30531         this.draw();
30532         
30533         this.fireEvent('rotate', this, 'left');
30534         
30535     },
30536     
30537     onRotateRight : function(e)
30538     {
30539         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30540             
30541             var minScale = this.thumbEl.getWidth() / this.minWidth;
30542         
30543             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30544             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30545             
30546             this.startScale = this.scale;
30547             
30548             while (this.getScaleLevel() < minScale){
30549             
30550                 this.scale = this.scale + 1;
30551                 
30552                 if(!this.zoomable()){
30553                     break;
30554                 }
30555                 
30556                 if(
30557                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30558                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30559                 ){
30560                     continue;
30561                 }
30562                 
30563                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30564
30565                 this.draw();
30566                 
30567                 return;
30568             }
30569             
30570             this.scale = this.startScale;
30571             
30572             this.onRotateFail();
30573             
30574             return false;
30575         }
30576         
30577         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30578
30579         if(this.isDocument){
30580             this.setThumbBoxSize();
30581             this.setThumbBoxPosition();
30582             this.setCanvasPosition();
30583         }
30584         
30585         this.draw();
30586         
30587         this.fireEvent('rotate', this, 'right');
30588     },
30589     
30590     onRotateFail : function()
30591     {
30592         this.errorEl.show(true);
30593         
30594         var _this = this;
30595         
30596         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30597     },
30598     
30599     draw : function()
30600     {
30601         this.previewEl.dom.innerHTML = '';
30602         
30603         var canvasEl = document.createElement("canvas");
30604         
30605         var contextEl = canvasEl.getContext("2d");
30606         
30607         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30608         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30609         var center = this.imageEl.OriginWidth / 2;
30610         
30611         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30612             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30613             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30614             center = this.imageEl.OriginHeight / 2;
30615         }
30616         
30617         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30618         
30619         contextEl.translate(center, center);
30620         contextEl.rotate(this.rotate * Math.PI / 180);
30621
30622         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30623         
30624         this.canvasEl = document.createElement("canvas");
30625         
30626         this.contextEl = this.canvasEl.getContext("2d");
30627         
30628         switch (this.rotate) {
30629             case 0 :
30630                 
30631                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30632                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30633                 
30634                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30635                 
30636                 break;
30637             case 90 : 
30638                 
30639                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30640                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30641                 
30642                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30643                     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);
30644                     break;
30645                 }
30646                 
30647                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30648                 
30649                 break;
30650             case 180 :
30651                 
30652                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30653                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30654                 
30655                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30656                     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);
30657                     break;
30658                 }
30659                 
30660                 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);
30661                 
30662                 break;
30663             case 270 :
30664                 
30665                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30666                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30667         
30668                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30669                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30670                     break;
30671                 }
30672                 
30673                 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);
30674                 
30675                 break;
30676             default : 
30677                 break;
30678         }
30679         
30680         this.previewEl.appendChild(this.canvasEl);
30681         
30682         this.setCanvasPosition();
30683     },
30684     
30685     crop : function()
30686     {
30687         if(!this.canvasLoaded){
30688             return;
30689         }
30690         
30691         var imageCanvas = document.createElement("canvas");
30692         
30693         var imageContext = imageCanvas.getContext("2d");
30694         
30695         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30696         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30697         
30698         var center = imageCanvas.width / 2;
30699         
30700         imageContext.translate(center, center);
30701         
30702         imageContext.rotate(this.rotate * Math.PI / 180);
30703         
30704         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30705         
30706         var canvas = document.createElement("canvas");
30707         
30708         var context = canvas.getContext("2d");
30709                 
30710         canvas.width = this.minWidth;
30711         canvas.height = this.minHeight;
30712
30713         switch (this.rotate) {
30714             case 0 :
30715                 
30716                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30717                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30718                 
30719                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30720                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30721                 
30722                 var targetWidth = this.minWidth - 2 * x;
30723                 var targetHeight = this.minHeight - 2 * y;
30724                 
30725                 var scale = 1;
30726                 
30727                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30728                     scale = targetWidth / width;
30729                 }
30730                 
30731                 if(x > 0 && y == 0){
30732                     scale = targetHeight / height;
30733                 }
30734                 
30735                 if(x > 0 && y > 0){
30736                     scale = targetWidth / width;
30737                     
30738                     if(width < height){
30739                         scale = targetHeight / height;
30740                     }
30741                 }
30742                 
30743                 context.scale(scale, scale);
30744                 
30745                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30746                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30747
30748                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30749                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30750
30751                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30752                 
30753                 break;
30754             case 90 : 
30755                 
30756                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30757                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30758                 
30759                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30760                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30761                 
30762                 var targetWidth = this.minWidth - 2 * x;
30763                 var targetHeight = this.minHeight - 2 * y;
30764                 
30765                 var scale = 1;
30766                 
30767                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30768                     scale = targetWidth / width;
30769                 }
30770                 
30771                 if(x > 0 && y == 0){
30772                     scale = targetHeight / height;
30773                 }
30774                 
30775                 if(x > 0 && y > 0){
30776                     scale = targetWidth / width;
30777                     
30778                     if(width < height){
30779                         scale = targetHeight / height;
30780                     }
30781                 }
30782                 
30783                 context.scale(scale, scale);
30784                 
30785                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30786                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30787
30788                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30789                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30790                 
30791                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30792                 
30793                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30794                 
30795                 break;
30796             case 180 :
30797                 
30798                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30799                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30800                 
30801                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30802                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30803                 
30804                 var targetWidth = this.minWidth - 2 * x;
30805                 var targetHeight = this.minHeight - 2 * y;
30806                 
30807                 var scale = 1;
30808                 
30809                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30810                     scale = targetWidth / width;
30811                 }
30812                 
30813                 if(x > 0 && y == 0){
30814                     scale = targetHeight / height;
30815                 }
30816                 
30817                 if(x > 0 && y > 0){
30818                     scale = targetWidth / width;
30819                     
30820                     if(width < height){
30821                         scale = targetHeight / height;
30822                     }
30823                 }
30824                 
30825                 context.scale(scale, scale);
30826                 
30827                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30828                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30829
30830                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30831                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30832
30833                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30834                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30835                 
30836                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30837                 
30838                 break;
30839             case 270 :
30840                 
30841                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30842                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30843                 
30844                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30845                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30846                 
30847                 var targetWidth = this.minWidth - 2 * x;
30848                 var targetHeight = this.minHeight - 2 * y;
30849                 
30850                 var scale = 1;
30851                 
30852                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30853                     scale = targetWidth / width;
30854                 }
30855                 
30856                 if(x > 0 && y == 0){
30857                     scale = targetHeight / height;
30858                 }
30859                 
30860                 if(x > 0 && y > 0){
30861                     scale = targetWidth / width;
30862                     
30863                     if(width < height){
30864                         scale = targetHeight / height;
30865                     }
30866                 }
30867                 
30868                 context.scale(scale, scale);
30869                 
30870                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30871                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30872
30873                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30874                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30875                 
30876                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30877                 
30878                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30879                 
30880                 break;
30881             default : 
30882                 break;
30883         }
30884         
30885         this.cropData = canvas.toDataURL(this.cropType);
30886         
30887         if(this.fireEvent('crop', this, this.cropData) !== false){
30888             this.process(this.file, this.cropData);
30889         }
30890         
30891         return;
30892         
30893     },
30894     
30895     setThumbBoxSize : function()
30896     {
30897         var width, height;
30898         
30899         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30900             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30901             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30902             
30903             this.minWidth = width;
30904             this.minHeight = height;
30905             
30906             if(this.rotate == 90 || this.rotate == 270){
30907                 this.minWidth = height;
30908                 this.minHeight = width;
30909             }
30910         }
30911         
30912         height = 300;
30913         width = Math.ceil(this.minWidth * height / this.minHeight);
30914         
30915         if(this.minWidth > this.minHeight){
30916             width = 300;
30917             height = Math.ceil(this.minHeight * width / this.minWidth);
30918         }
30919         
30920         this.thumbEl.setStyle({
30921             width : width + 'px',
30922             height : height + 'px'
30923         });
30924
30925         return;
30926             
30927     },
30928     
30929     setThumbBoxPosition : function()
30930     {
30931         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30932         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30933         
30934         this.thumbEl.setLeft(x);
30935         this.thumbEl.setTop(y);
30936         
30937     },
30938     
30939     baseRotateLevel : function()
30940     {
30941         this.baseRotate = 1;
30942         
30943         if(
30944                 typeof(this.exif) != 'undefined' &&
30945                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30946                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30947         ){
30948             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30949         }
30950         
30951         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30952         
30953     },
30954     
30955     baseScaleLevel : function()
30956     {
30957         var width, height;
30958         
30959         if(this.isDocument){
30960             
30961             if(this.baseRotate == 6 || this.baseRotate == 8){
30962             
30963                 height = this.thumbEl.getHeight();
30964                 this.baseScale = height / this.imageEl.OriginWidth;
30965
30966                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30967                     width = this.thumbEl.getWidth();
30968                     this.baseScale = width / this.imageEl.OriginHeight;
30969                 }
30970
30971                 return;
30972             }
30973
30974             height = this.thumbEl.getHeight();
30975             this.baseScale = height / this.imageEl.OriginHeight;
30976
30977             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30978                 width = this.thumbEl.getWidth();
30979                 this.baseScale = width / this.imageEl.OriginWidth;
30980             }
30981
30982             return;
30983         }
30984         
30985         if(this.baseRotate == 6 || this.baseRotate == 8){
30986             
30987             width = this.thumbEl.getHeight();
30988             this.baseScale = width / this.imageEl.OriginHeight;
30989             
30990             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30991                 height = this.thumbEl.getWidth();
30992                 this.baseScale = height / this.imageEl.OriginHeight;
30993             }
30994             
30995             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30996                 height = this.thumbEl.getWidth();
30997                 this.baseScale = height / this.imageEl.OriginHeight;
30998                 
30999                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31000                     width = this.thumbEl.getHeight();
31001                     this.baseScale = width / this.imageEl.OriginWidth;
31002                 }
31003             }
31004             
31005             return;
31006         }
31007         
31008         width = this.thumbEl.getWidth();
31009         this.baseScale = width / this.imageEl.OriginWidth;
31010         
31011         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31012             height = this.thumbEl.getHeight();
31013             this.baseScale = height / this.imageEl.OriginHeight;
31014         }
31015         
31016         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31017             
31018             height = this.thumbEl.getHeight();
31019             this.baseScale = height / this.imageEl.OriginHeight;
31020             
31021             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31022                 width = this.thumbEl.getWidth();
31023                 this.baseScale = width / this.imageEl.OriginWidth;
31024             }
31025             
31026         }
31027         
31028         return;
31029     },
31030     
31031     getScaleLevel : function()
31032     {
31033         return this.baseScale * Math.pow(1.1, this.scale);
31034     },
31035     
31036     onTouchStart : function(e)
31037     {
31038         if(!this.canvasLoaded){
31039             this.beforeSelectFile(e);
31040             return;
31041         }
31042         
31043         var touches = e.browserEvent.touches;
31044         
31045         if(!touches){
31046             return;
31047         }
31048         
31049         if(touches.length == 1){
31050             this.onMouseDown(e);
31051             return;
31052         }
31053         
31054         if(touches.length != 2){
31055             return;
31056         }
31057         
31058         var coords = [];
31059         
31060         for(var i = 0, finger; finger = touches[i]; i++){
31061             coords.push(finger.pageX, finger.pageY);
31062         }
31063         
31064         var x = Math.pow(coords[0] - coords[2], 2);
31065         var y = Math.pow(coords[1] - coords[3], 2);
31066         
31067         this.startDistance = Math.sqrt(x + y);
31068         
31069         this.startScale = this.scale;
31070         
31071         this.pinching = true;
31072         this.dragable = false;
31073         
31074     },
31075     
31076     onTouchMove : function(e)
31077     {
31078         if(!this.pinching && !this.dragable){
31079             return;
31080         }
31081         
31082         var touches = e.browserEvent.touches;
31083         
31084         if(!touches){
31085             return;
31086         }
31087         
31088         if(this.dragable){
31089             this.onMouseMove(e);
31090             return;
31091         }
31092         
31093         var coords = [];
31094         
31095         for(var i = 0, finger; finger = touches[i]; i++){
31096             coords.push(finger.pageX, finger.pageY);
31097         }
31098         
31099         var x = Math.pow(coords[0] - coords[2], 2);
31100         var y = Math.pow(coords[1] - coords[3], 2);
31101         
31102         this.endDistance = Math.sqrt(x + y);
31103         
31104         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31105         
31106         if(!this.zoomable()){
31107             this.scale = this.startScale;
31108             return;
31109         }
31110         
31111         this.draw();
31112         
31113     },
31114     
31115     onTouchEnd : function(e)
31116     {
31117         this.pinching = false;
31118         this.dragable = false;
31119         
31120     },
31121     
31122     process : function(file, crop)
31123     {
31124         if(this.loadMask){
31125             this.maskEl.mask(this.loadingText);
31126         }
31127         
31128         this.xhr = new XMLHttpRequest();
31129         
31130         file.xhr = this.xhr;
31131
31132         this.xhr.open(this.method, this.url, true);
31133         
31134         var headers = {
31135             "Accept": "application/json",
31136             "Cache-Control": "no-cache",
31137             "X-Requested-With": "XMLHttpRequest"
31138         };
31139         
31140         for (var headerName in headers) {
31141             var headerValue = headers[headerName];
31142             if (headerValue) {
31143                 this.xhr.setRequestHeader(headerName, headerValue);
31144             }
31145         }
31146         
31147         var _this = this;
31148         
31149         this.xhr.onload = function()
31150         {
31151             _this.xhrOnLoad(_this.xhr);
31152         }
31153         
31154         this.xhr.onerror = function()
31155         {
31156             _this.xhrOnError(_this.xhr);
31157         }
31158         
31159         var formData = new FormData();
31160
31161         formData.append('returnHTML', 'NO');
31162         
31163         if(crop){
31164             formData.append('crop', crop);
31165         }
31166         
31167         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31168             formData.append(this.paramName, file, file.name);
31169         }
31170         
31171         if(typeof(file.filename) != 'undefined'){
31172             formData.append('filename', file.filename);
31173         }
31174         
31175         if(typeof(file.mimetype) != 'undefined'){
31176             formData.append('mimetype', file.mimetype);
31177         }
31178         
31179         if(this.fireEvent('arrange', this, formData) != false){
31180             this.xhr.send(formData);
31181         };
31182     },
31183     
31184     xhrOnLoad : function(xhr)
31185     {
31186         if(this.loadMask){
31187             this.maskEl.unmask();
31188         }
31189         
31190         if (xhr.readyState !== 4) {
31191             this.fireEvent('exception', this, xhr);
31192             return;
31193         }
31194
31195         var response = Roo.decode(xhr.responseText);
31196         
31197         if(!response.success){
31198             this.fireEvent('exception', this, xhr);
31199             return;
31200         }
31201         
31202         var response = Roo.decode(xhr.responseText);
31203         
31204         this.fireEvent('upload', this, response);
31205         
31206     },
31207     
31208     xhrOnError : function()
31209     {
31210         if(this.loadMask){
31211             this.maskEl.unmask();
31212         }
31213         
31214         Roo.log('xhr on error');
31215         
31216         var response = Roo.decode(xhr.responseText);
31217           
31218         Roo.log(response);
31219         
31220     },
31221     
31222     prepare : function(file)
31223     {   
31224         if(this.loadMask){
31225             this.maskEl.mask(this.loadingText);
31226         }
31227         
31228         this.file = false;
31229         this.exif = {};
31230         
31231         if(typeof(file) === 'string'){
31232             this.loadCanvas(file);
31233             return;
31234         }
31235         
31236         if(!file || !this.urlAPI){
31237             return;
31238         }
31239         
31240         this.file = file;
31241         this.cropType = file.type;
31242         
31243         var _this = this;
31244         
31245         if(this.fireEvent('prepare', this, this.file) != false){
31246             
31247             var reader = new FileReader();
31248             
31249             reader.onload = function (e) {
31250                 if (e.target.error) {
31251                     Roo.log(e.target.error);
31252                     return;
31253                 }
31254                 
31255                 var buffer = e.target.result,
31256                     dataView = new DataView(buffer),
31257                     offset = 2,
31258                     maxOffset = dataView.byteLength - 4,
31259                     markerBytes,
31260                     markerLength;
31261                 
31262                 if (dataView.getUint16(0) === 0xffd8) {
31263                     while (offset < maxOffset) {
31264                         markerBytes = dataView.getUint16(offset);
31265                         
31266                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31267                             markerLength = dataView.getUint16(offset + 2) + 2;
31268                             if (offset + markerLength > dataView.byteLength) {
31269                                 Roo.log('Invalid meta data: Invalid segment size.');
31270                                 break;
31271                             }
31272                             
31273                             if(markerBytes == 0xffe1){
31274                                 _this.parseExifData(
31275                                     dataView,
31276                                     offset,
31277                                     markerLength
31278                                 );
31279                             }
31280                             
31281                             offset += markerLength;
31282                             
31283                             continue;
31284                         }
31285                         
31286                         break;
31287                     }
31288                     
31289                 }
31290                 
31291                 var url = _this.urlAPI.createObjectURL(_this.file);
31292                 
31293                 _this.loadCanvas(url);
31294                 
31295                 return;
31296             }
31297             
31298             reader.readAsArrayBuffer(this.file);
31299             
31300         }
31301         
31302     },
31303     
31304     parseExifData : function(dataView, offset, length)
31305     {
31306         var tiffOffset = offset + 10,
31307             littleEndian,
31308             dirOffset;
31309     
31310         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31311             // No Exif data, might be XMP data instead
31312             return;
31313         }
31314         
31315         // Check for the ASCII code for "Exif" (0x45786966):
31316         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31317             // No Exif data, might be XMP data instead
31318             return;
31319         }
31320         if (tiffOffset + 8 > dataView.byteLength) {
31321             Roo.log('Invalid Exif data: Invalid segment size.');
31322             return;
31323         }
31324         // Check for the two null bytes:
31325         if (dataView.getUint16(offset + 8) !== 0x0000) {
31326             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31327             return;
31328         }
31329         // Check the byte alignment:
31330         switch (dataView.getUint16(tiffOffset)) {
31331         case 0x4949:
31332             littleEndian = true;
31333             break;
31334         case 0x4D4D:
31335             littleEndian = false;
31336             break;
31337         default:
31338             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31339             return;
31340         }
31341         // Check for the TIFF tag marker (0x002A):
31342         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31343             Roo.log('Invalid Exif data: Missing TIFF marker.');
31344             return;
31345         }
31346         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31347         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31348         
31349         this.parseExifTags(
31350             dataView,
31351             tiffOffset,
31352             tiffOffset + dirOffset,
31353             littleEndian
31354         );
31355     },
31356     
31357     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31358     {
31359         var tagsNumber,
31360             dirEndOffset,
31361             i;
31362         if (dirOffset + 6 > dataView.byteLength) {
31363             Roo.log('Invalid Exif data: Invalid directory offset.');
31364             return;
31365         }
31366         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31367         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31368         if (dirEndOffset + 4 > dataView.byteLength) {
31369             Roo.log('Invalid Exif data: Invalid directory size.');
31370             return;
31371         }
31372         for (i = 0; i < tagsNumber; i += 1) {
31373             this.parseExifTag(
31374                 dataView,
31375                 tiffOffset,
31376                 dirOffset + 2 + 12 * i, // tag offset
31377                 littleEndian
31378             );
31379         }
31380         // Return the offset to the next directory:
31381         return dataView.getUint32(dirEndOffset, littleEndian);
31382     },
31383     
31384     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31385     {
31386         var tag = dataView.getUint16(offset, littleEndian);
31387         
31388         this.exif[tag] = this.getExifValue(
31389             dataView,
31390             tiffOffset,
31391             offset,
31392             dataView.getUint16(offset + 2, littleEndian), // tag type
31393             dataView.getUint32(offset + 4, littleEndian), // tag length
31394             littleEndian
31395         );
31396     },
31397     
31398     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31399     {
31400         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31401             tagSize,
31402             dataOffset,
31403             values,
31404             i,
31405             str,
31406             c;
31407     
31408         if (!tagType) {
31409             Roo.log('Invalid Exif data: Invalid tag type.');
31410             return;
31411         }
31412         
31413         tagSize = tagType.size * length;
31414         // Determine if the value is contained in the dataOffset bytes,
31415         // or if the value at the dataOffset is a pointer to the actual data:
31416         dataOffset = tagSize > 4 ?
31417                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31418         if (dataOffset + tagSize > dataView.byteLength) {
31419             Roo.log('Invalid Exif data: Invalid data offset.');
31420             return;
31421         }
31422         if (length === 1) {
31423             return tagType.getValue(dataView, dataOffset, littleEndian);
31424         }
31425         values = [];
31426         for (i = 0; i < length; i += 1) {
31427             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31428         }
31429         
31430         if (tagType.ascii) {
31431             str = '';
31432             // Concatenate the chars:
31433             for (i = 0; i < values.length; i += 1) {
31434                 c = values[i];
31435                 // Ignore the terminating NULL byte(s):
31436                 if (c === '\u0000') {
31437                     break;
31438                 }
31439                 str += c;
31440             }
31441             return str;
31442         }
31443         return values;
31444     }
31445     
31446 });
31447
31448 Roo.apply(Roo.bootstrap.UploadCropbox, {
31449     tags : {
31450         'Orientation': 0x0112
31451     },
31452     
31453     Orientation: {
31454             1: 0, //'top-left',
31455 //            2: 'top-right',
31456             3: 180, //'bottom-right',
31457 //            4: 'bottom-left',
31458 //            5: 'left-top',
31459             6: 90, //'right-top',
31460 //            7: 'right-bottom',
31461             8: 270 //'left-bottom'
31462     },
31463     
31464     exifTagTypes : {
31465         // byte, 8-bit unsigned int:
31466         1: {
31467             getValue: function (dataView, dataOffset) {
31468                 return dataView.getUint8(dataOffset);
31469             },
31470             size: 1
31471         },
31472         // ascii, 8-bit byte:
31473         2: {
31474             getValue: function (dataView, dataOffset) {
31475                 return String.fromCharCode(dataView.getUint8(dataOffset));
31476             },
31477             size: 1,
31478             ascii: true
31479         },
31480         // short, 16 bit int:
31481         3: {
31482             getValue: function (dataView, dataOffset, littleEndian) {
31483                 return dataView.getUint16(dataOffset, littleEndian);
31484             },
31485             size: 2
31486         },
31487         // long, 32 bit int:
31488         4: {
31489             getValue: function (dataView, dataOffset, littleEndian) {
31490                 return dataView.getUint32(dataOffset, littleEndian);
31491             },
31492             size: 4
31493         },
31494         // rational = two long values, first is numerator, second is denominator:
31495         5: {
31496             getValue: function (dataView, dataOffset, littleEndian) {
31497                 return dataView.getUint32(dataOffset, littleEndian) /
31498                     dataView.getUint32(dataOffset + 4, littleEndian);
31499             },
31500             size: 8
31501         },
31502         // slong, 32 bit signed int:
31503         9: {
31504             getValue: function (dataView, dataOffset, littleEndian) {
31505                 return dataView.getInt32(dataOffset, littleEndian);
31506             },
31507             size: 4
31508         },
31509         // srational, two slongs, first is numerator, second is denominator:
31510         10: {
31511             getValue: function (dataView, dataOffset, littleEndian) {
31512                 return dataView.getInt32(dataOffset, littleEndian) /
31513                     dataView.getInt32(dataOffset + 4, littleEndian);
31514             },
31515             size: 8
31516         }
31517     },
31518     
31519     footer : {
31520         STANDARD : [
31521             {
31522                 tag : 'div',
31523                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31524                 action : 'rotate-left',
31525                 cn : [
31526                     {
31527                         tag : 'button',
31528                         cls : 'btn btn-default',
31529                         html : '<i class="fa fa-undo"></i>'
31530                     }
31531                 ]
31532             },
31533             {
31534                 tag : 'div',
31535                 cls : 'btn-group roo-upload-cropbox-picture',
31536                 action : 'picture',
31537                 cn : [
31538                     {
31539                         tag : 'button',
31540                         cls : 'btn btn-default',
31541                         html : '<i class="fa fa-picture-o"></i>'
31542                     }
31543                 ]
31544             },
31545             {
31546                 tag : 'div',
31547                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31548                 action : 'rotate-right',
31549                 cn : [
31550                     {
31551                         tag : 'button',
31552                         cls : 'btn btn-default',
31553                         html : '<i class="fa fa-repeat"></i>'
31554                     }
31555                 ]
31556             }
31557         ],
31558         DOCUMENT : [
31559             {
31560                 tag : 'div',
31561                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31562                 action : 'rotate-left',
31563                 cn : [
31564                     {
31565                         tag : 'button',
31566                         cls : 'btn btn-default',
31567                         html : '<i class="fa fa-undo"></i>'
31568                     }
31569                 ]
31570             },
31571             {
31572                 tag : 'div',
31573                 cls : 'btn-group roo-upload-cropbox-download',
31574                 action : 'download',
31575                 cn : [
31576                     {
31577                         tag : 'button',
31578                         cls : 'btn btn-default',
31579                         html : '<i class="fa fa-download"></i>'
31580                     }
31581                 ]
31582             },
31583             {
31584                 tag : 'div',
31585                 cls : 'btn-group roo-upload-cropbox-crop',
31586                 action : 'crop',
31587                 cn : [
31588                     {
31589                         tag : 'button',
31590                         cls : 'btn btn-default',
31591                         html : '<i class="fa fa-crop"></i>'
31592                     }
31593                 ]
31594             },
31595             {
31596                 tag : 'div',
31597                 cls : 'btn-group roo-upload-cropbox-trash',
31598                 action : 'trash',
31599                 cn : [
31600                     {
31601                         tag : 'button',
31602                         cls : 'btn btn-default',
31603                         html : '<i class="fa fa-trash"></i>'
31604                     }
31605                 ]
31606             },
31607             {
31608                 tag : 'div',
31609                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31610                 action : 'rotate-right',
31611                 cn : [
31612                     {
31613                         tag : 'button',
31614                         cls : 'btn btn-default',
31615                         html : '<i class="fa fa-repeat"></i>'
31616                     }
31617                 ]
31618             }
31619         ],
31620         ROTATOR : [
31621             {
31622                 tag : 'div',
31623                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31624                 action : 'rotate-left',
31625                 cn : [
31626                     {
31627                         tag : 'button',
31628                         cls : 'btn btn-default',
31629                         html : '<i class="fa fa-undo"></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     }
31647 });
31648
31649 /*
31650 * Licence: LGPL
31651 */
31652
31653 /**
31654  * @class Roo.bootstrap.DocumentManager
31655  * @extends Roo.bootstrap.Component
31656  * Bootstrap DocumentManager class
31657  * @cfg {String} paramName default 'imageUpload'
31658  * @cfg {String} toolTipName default 'filename'
31659  * @cfg {String} method default POST
31660  * @cfg {String} url action url
31661  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31662  * @cfg {Boolean} multiple multiple upload default true
31663  * @cfg {Number} thumbSize default 300
31664  * @cfg {String} fieldLabel
31665  * @cfg {Number} labelWidth default 4
31666  * @cfg {String} labelAlign (left|top) default left
31667  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31668 * @cfg {Number} labellg set the width of label (1-12)
31669  * @cfg {Number} labelmd set the width of label (1-12)
31670  * @cfg {Number} labelsm set the width of label (1-12)
31671  * @cfg {Number} labelxs set the width of label (1-12)
31672  * 
31673  * @constructor
31674  * Create a new DocumentManager
31675  * @param {Object} config The config object
31676  */
31677
31678 Roo.bootstrap.DocumentManager = function(config){
31679     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31680     
31681     this.files = [];
31682     this.delegates = [];
31683     
31684     this.addEvents({
31685         /**
31686          * @event initial
31687          * Fire when initial the DocumentManager
31688          * @param {Roo.bootstrap.DocumentManager} this
31689          */
31690         "initial" : true,
31691         /**
31692          * @event inspect
31693          * inspect selected file
31694          * @param {Roo.bootstrap.DocumentManager} this
31695          * @param {File} file
31696          */
31697         "inspect" : true,
31698         /**
31699          * @event exception
31700          * Fire when xhr load exception
31701          * @param {Roo.bootstrap.DocumentManager} this
31702          * @param {XMLHttpRequest} xhr
31703          */
31704         "exception" : true,
31705         /**
31706          * @event afterupload
31707          * Fire when xhr load exception
31708          * @param {Roo.bootstrap.DocumentManager} this
31709          * @param {XMLHttpRequest} xhr
31710          */
31711         "afterupload" : true,
31712         /**
31713          * @event prepare
31714          * prepare the form data
31715          * @param {Roo.bootstrap.DocumentManager} this
31716          * @param {Object} formData
31717          */
31718         "prepare" : true,
31719         /**
31720          * @event remove
31721          * Fire when remove the file
31722          * @param {Roo.bootstrap.DocumentManager} this
31723          * @param {Object} file
31724          */
31725         "remove" : true,
31726         /**
31727          * @event refresh
31728          * Fire after refresh the file
31729          * @param {Roo.bootstrap.DocumentManager} this
31730          */
31731         "refresh" : true,
31732         /**
31733          * @event click
31734          * Fire after click the image
31735          * @param {Roo.bootstrap.DocumentManager} this
31736          * @param {Object} file
31737          */
31738         "click" : true,
31739         /**
31740          * @event edit
31741          * Fire when upload a image and editable set to true
31742          * @param {Roo.bootstrap.DocumentManager} this
31743          * @param {Object} file
31744          */
31745         "edit" : true,
31746         /**
31747          * @event beforeselectfile
31748          * Fire before select file
31749          * @param {Roo.bootstrap.DocumentManager} this
31750          */
31751         "beforeselectfile" : true,
31752         /**
31753          * @event process
31754          * Fire before process file
31755          * @param {Roo.bootstrap.DocumentManager} this
31756          * @param {Object} file
31757          */
31758         "process" : true,
31759         /**
31760          * @event previewrendered
31761          * Fire when preview rendered
31762          * @param {Roo.bootstrap.DocumentManager} this
31763          * @param {Object} file
31764          */
31765         "previewrendered" : true,
31766         /**
31767          */
31768         "previewResize" : true
31769         
31770     });
31771 };
31772
31773 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31774     
31775     boxes : 0,
31776     inputName : '',
31777     thumbSize : 300,
31778     multiple : true,
31779     files : false,
31780     method : 'POST',
31781     url : '',
31782     paramName : 'imageUpload',
31783     toolTipName : 'filename',
31784     fieldLabel : '',
31785     labelWidth : 4,
31786     labelAlign : 'left',
31787     editable : true,
31788     delegates : false,
31789     xhr : false, 
31790     
31791     labellg : 0,
31792     labelmd : 0,
31793     labelsm : 0,
31794     labelxs : 0,
31795     
31796     getAutoCreate : function()
31797     {   
31798         var managerWidget = {
31799             tag : 'div',
31800             cls : 'roo-document-manager',
31801             cn : [
31802                 {
31803                     tag : 'input',
31804                     cls : 'roo-document-manager-selector',
31805                     type : 'file'
31806                 },
31807                 {
31808                     tag : 'div',
31809                     cls : 'roo-document-manager-uploader',
31810                     cn : [
31811                         {
31812                             tag : 'div',
31813                             cls : 'roo-document-manager-upload-btn',
31814                             html : '<i class="fa fa-plus"></i>'
31815                         }
31816                     ]
31817                     
31818                 }
31819             ]
31820         };
31821         
31822         var content = [
31823             {
31824                 tag : 'div',
31825                 cls : 'column col-md-12',
31826                 cn : managerWidget
31827             }
31828         ];
31829         
31830         if(this.fieldLabel.length){
31831             
31832             content = [
31833                 {
31834                     tag : 'div',
31835                     cls : 'column col-md-12',
31836                     html : this.fieldLabel
31837                 },
31838                 {
31839                     tag : 'div',
31840                     cls : 'column col-md-12',
31841                     cn : managerWidget
31842                 }
31843             ];
31844
31845             if(this.labelAlign == 'left'){
31846                 content = [
31847                     {
31848                         tag : 'div',
31849                         cls : 'column',
31850                         html : this.fieldLabel
31851                     },
31852                     {
31853                         tag : 'div',
31854                         cls : 'column',
31855                         cn : managerWidget
31856                     }
31857                 ];
31858                 
31859                 if(this.labelWidth > 12){
31860                     content[0].style = "width: " + this.labelWidth + 'px';
31861                 }
31862
31863                 if(this.labelWidth < 13 && this.labelmd == 0){
31864                     this.labelmd = this.labelWidth;
31865                 }
31866
31867                 if(this.labellg > 0){
31868                     content[0].cls += ' col-lg-' + this.labellg;
31869                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31870                 }
31871
31872                 if(this.labelmd > 0){
31873                     content[0].cls += ' col-md-' + this.labelmd;
31874                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31875                 }
31876
31877                 if(this.labelsm > 0){
31878                     content[0].cls += ' col-sm-' + this.labelsm;
31879                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31880                 }
31881
31882                 if(this.labelxs > 0){
31883                     content[0].cls += ' col-xs-' + this.labelxs;
31884                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31885                 }
31886                 
31887             }
31888         }
31889         
31890         var cfg = {
31891             tag : 'div',
31892             cls : 'row clearfix',
31893             cn : content
31894         };
31895         
31896         return cfg;
31897         
31898     },
31899     
31900     initEvents : function()
31901     {
31902         this.managerEl = this.el.select('.roo-document-manager', true).first();
31903         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31904         
31905         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31906         this.selectorEl.hide();
31907         
31908         if(this.multiple){
31909             this.selectorEl.attr('multiple', 'multiple');
31910         }
31911         
31912         this.selectorEl.on('change', this.onFileSelected, this);
31913         
31914         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31915         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31916         
31917         this.uploader.on('click', this.onUploaderClick, this);
31918         
31919         this.renderProgressDialog();
31920         
31921         var _this = this;
31922         
31923         window.addEventListener("resize", function() { _this.refresh(); } );
31924         
31925         this.fireEvent('initial', this);
31926     },
31927     
31928     renderProgressDialog : function()
31929     {
31930         var _this = this;
31931         
31932         this.progressDialog = new Roo.bootstrap.Modal({
31933             cls : 'roo-document-manager-progress-dialog',
31934             allow_close : false,
31935             animate : false,
31936             title : '',
31937             buttons : [
31938                 {
31939                     name  :'cancel',
31940                     weight : 'danger',
31941                     html : 'Cancel'
31942                 }
31943             ], 
31944             listeners : { 
31945                 btnclick : function() {
31946                     _this.uploadCancel();
31947                     this.hide();
31948                 }
31949             }
31950         });
31951          
31952         this.progressDialog.render(Roo.get(document.body));
31953          
31954         this.progress = new Roo.bootstrap.Progress({
31955             cls : 'roo-document-manager-progress',
31956             active : true,
31957             striped : true
31958         });
31959         
31960         this.progress.render(this.progressDialog.getChildContainer());
31961         
31962         this.progressBar = new Roo.bootstrap.ProgressBar({
31963             cls : 'roo-document-manager-progress-bar',
31964             aria_valuenow : 0,
31965             aria_valuemin : 0,
31966             aria_valuemax : 12,
31967             panel : 'success'
31968         });
31969         
31970         this.progressBar.render(this.progress.getChildContainer());
31971     },
31972     
31973     onUploaderClick : function(e)
31974     {
31975         e.preventDefault();
31976      
31977         if(this.fireEvent('beforeselectfile', this) != false){
31978             this.selectorEl.dom.click();
31979         }
31980         
31981     },
31982     
31983     onFileSelected : function(e)
31984     {
31985         e.preventDefault();
31986         
31987         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31988             return;
31989         }
31990         
31991         Roo.each(this.selectorEl.dom.files, function(file){
31992             if(this.fireEvent('inspect', this, file) != false){
31993                 this.files.push(file);
31994             }
31995         }, this);
31996         
31997         this.queue();
31998         
31999     },
32000     
32001     queue : function()
32002     {
32003         this.selectorEl.dom.value = '';
32004         
32005         if(!this.files || !this.files.length){
32006             return;
32007         }
32008         
32009         if(this.boxes > 0 && this.files.length > this.boxes){
32010             this.files = this.files.slice(0, this.boxes);
32011         }
32012         
32013         this.uploader.show();
32014         
32015         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32016             this.uploader.hide();
32017         }
32018         
32019         var _this = this;
32020         
32021         var files = [];
32022         
32023         var docs = [];
32024         
32025         Roo.each(this.files, function(file){
32026             
32027             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32028                 var f = this.renderPreview(file);
32029                 files.push(f);
32030                 return;
32031             }
32032             
32033             if(file.type.indexOf('image') != -1){
32034                 this.delegates.push(
32035                     (function(){
32036                         _this.process(file);
32037                     }).createDelegate(this)
32038                 );
32039         
32040                 return;
32041             }
32042             
32043             docs.push(
32044                 (function(){
32045                     _this.process(file);
32046                 }).createDelegate(this)
32047             );
32048             
32049         }, this);
32050         
32051         this.files = files;
32052         
32053         this.delegates = this.delegates.concat(docs);
32054         
32055         if(!this.delegates.length){
32056             this.refresh();
32057             return;
32058         }
32059         
32060         this.progressBar.aria_valuemax = this.delegates.length;
32061         
32062         this.arrange();
32063         
32064         return;
32065     },
32066     
32067     arrange : function()
32068     {
32069         if(!this.delegates.length){
32070             this.progressDialog.hide();
32071             this.refresh();
32072             return;
32073         }
32074         
32075         var delegate = this.delegates.shift();
32076         
32077         this.progressDialog.show();
32078         
32079         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32080         
32081         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32082         
32083         delegate();
32084     },
32085     
32086     refresh : function()
32087     {
32088         this.uploader.show();
32089         
32090         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32091             this.uploader.hide();
32092         }
32093         
32094         Roo.isTouch ? this.closable(false) : this.closable(true);
32095         
32096         this.fireEvent('refresh', this);
32097     },
32098     
32099     onRemove : function(e, el, o)
32100     {
32101         e.preventDefault();
32102         
32103         this.fireEvent('remove', this, o);
32104         
32105     },
32106     
32107     remove : function(o)
32108     {
32109         var files = [];
32110         
32111         Roo.each(this.files, function(file){
32112             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32113                 files.push(file);
32114                 return;
32115             }
32116
32117             o.target.remove();
32118
32119         }, this);
32120         
32121         this.files = files;
32122         
32123         this.refresh();
32124     },
32125     
32126     clear : function()
32127     {
32128         Roo.each(this.files, function(file){
32129             if(!file.target){
32130                 return;
32131             }
32132             
32133             file.target.remove();
32134
32135         }, this);
32136         
32137         this.files = [];
32138         
32139         this.refresh();
32140     },
32141     
32142     onClick : function(e, el, o)
32143     {
32144         e.preventDefault();
32145         
32146         this.fireEvent('click', this, o);
32147         
32148     },
32149     
32150     closable : function(closable)
32151     {
32152         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32153             
32154             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32155             
32156             if(closable){
32157                 el.show();
32158                 return;
32159             }
32160             
32161             el.hide();
32162             
32163         }, this);
32164     },
32165     
32166     xhrOnLoad : function(xhr)
32167     {
32168         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32169             el.remove();
32170         }, this);
32171         
32172         if (xhr.readyState !== 4) {
32173             this.arrange();
32174             this.fireEvent('exception', this, xhr);
32175             return;
32176         }
32177
32178         var response = Roo.decode(xhr.responseText);
32179         
32180         if(!response.success){
32181             this.arrange();
32182             this.fireEvent('exception', this, xhr);
32183             return;
32184         }
32185         
32186         var file = this.renderPreview(response.data);
32187         
32188         this.files.push(file);
32189         
32190         this.arrange();
32191         
32192         this.fireEvent('afterupload', this, xhr);
32193         
32194     },
32195     
32196     xhrOnError : function(xhr)
32197     {
32198         Roo.log('xhr on error');
32199         
32200         var response = Roo.decode(xhr.responseText);
32201           
32202         Roo.log(response);
32203         
32204         this.arrange();
32205     },
32206     
32207     process : function(file)
32208     {
32209         if(this.fireEvent('process', this, file) !== false){
32210             if(this.editable && file.type.indexOf('image') != -1){
32211                 this.fireEvent('edit', this, file);
32212                 return;
32213             }
32214
32215             this.uploadStart(file, false);
32216
32217             return;
32218         }
32219         
32220     },
32221     
32222     uploadStart : function(file, crop)
32223     {
32224         this.xhr = new XMLHttpRequest();
32225         
32226         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32227             this.arrange();
32228             return;
32229         }
32230         
32231         file.xhr = this.xhr;
32232             
32233         this.managerEl.createChild({
32234             tag : 'div',
32235             cls : 'roo-document-manager-loading',
32236             cn : [
32237                 {
32238                     tag : 'div',
32239                     tooltip : file.name,
32240                     cls : 'roo-document-manager-thumb',
32241                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32242                 }
32243             ]
32244
32245         });
32246
32247         this.xhr.open(this.method, this.url, true);
32248         
32249         var headers = {
32250             "Accept": "application/json",
32251             "Cache-Control": "no-cache",
32252             "X-Requested-With": "XMLHttpRequest"
32253         };
32254         
32255         for (var headerName in headers) {
32256             var headerValue = headers[headerName];
32257             if (headerValue) {
32258                 this.xhr.setRequestHeader(headerName, headerValue);
32259             }
32260         }
32261         
32262         var _this = this;
32263         
32264         this.xhr.onload = function()
32265         {
32266             _this.xhrOnLoad(_this.xhr);
32267         }
32268         
32269         this.xhr.onerror = function()
32270         {
32271             _this.xhrOnError(_this.xhr);
32272         }
32273         
32274         var formData = new FormData();
32275
32276         formData.append('returnHTML', 'NO');
32277         
32278         if(crop){
32279             formData.append('crop', crop);
32280         }
32281         
32282         formData.append(this.paramName, file, file.name);
32283         
32284         var options = {
32285             file : file, 
32286             manually : false
32287         };
32288         
32289         if(this.fireEvent('prepare', this, formData, options) != false){
32290             
32291             if(options.manually){
32292                 return;
32293             }
32294             
32295             this.xhr.send(formData);
32296             return;
32297         };
32298         
32299         this.uploadCancel();
32300     },
32301     
32302     uploadCancel : function()
32303     {
32304         if (this.xhr) {
32305             this.xhr.abort();
32306         }
32307         
32308         this.delegates = [];
32309         
32310         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32311             el.remove();
32312         }, this);
32313         
32314         this.arrange();
32315     },
32316     
32317     renderPreview : function(file)
32318     {
32319         if(typeof(file.target) != 'undefined' && file.target){
32320             return file;
32321         }
32322         
32323         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32324         
32325         var previewEl = this.managerEl.createChild({
32326             tag : 'div',
32327             cls : 'roo-document-manager-preview',
32328             cn : [
32329                 {
32330                     tag : 'div',
32331                     tooltip : file[this.toolTipName],
32332                     cls : 'roo-document-manager-thumb',
32333                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32334                 },
32335                 {
32336                     tag : 'button',
32337                     cls : 'close',
32338                     html : '<i class="fa fa-times-circle"></i>'
32339                 }
32340             ]
32341         });
32342
32343         var close = previewEl.select('button.close', true).first();
32344
32345         close.on('click', this.onRemove, this, file);
32346
32347         file.target = previewEl;
32348
32349         var image = previewEl.select('img', true).first();
32350         
32351         var _this = this;
32352         
32353         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32354         
32355         image.on('click', this.onClick, this, file);
32356         
32357         this.fireEvent('previewrendered', this, file);
32358         
32359         return file;
32360         
32361     },
32362     
32363     onPreviewLoad : function(file, image)
32364     {
32365         if(typeof(file.target) == 'undefined' || !file.target){
32366             return;
32367         }
32368         
32369         var width = image.dom.naturalWidth || image.dom.width;
32370         var height = image.dom.naturalHeight || image.dom.height;
32371         
32372         if(!this.previewResize) {
32373             return;
32374         }
32375         
32376         if(width > height){
32377             file.target.addClass('wide');
32378             return;
32379         }
32380         
32381         file.target.addClass('tall');
32382         return;
32383         
32384     },
32385     
32386     uploadFromSource : function(file, crop)
32387     {
32388         this.xhr = new XMLHttpRequest();
32389         
32390         this.managerEl.createChild({
32391             tag : 'div',
32392             cls : 'roo-document-manager-loading',
32393             cn : [
32394                 {
32395                     tag : 'div',
32396                     tooltip : file.name,
32397                     cls : 'roo-document-manager-thumb',
32398                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32399                 }
32400             ]
32401
32402         });
32403
32404         this.xhr.open(this.method, this.url, true);
32405         
32406         var headers = {
32407             "Accept": "application/json",
32408             "Cache-Control": "no-cache",
32409             "X-Requested-With": "XMLHttpRequest"
32410         };
32411         
32412         for (var headerName in headers) {
32413             var headerValue = headers[headerName];
32414             if (headerValue) {
32415                 this.xhr.setRequestHeader(headerName, headerValue);
32416             }
32417         }
32418         
32419         var _this = this;
32420         
32421         this.xhr.onload = function()
32422         {
32423             _this.xhrOnLoad(_this.xhr);
32424         }
32425         
32426         this.xhr.onerror = function()
32427         {
32428             _this.xhrOnError(_this.xhr);
32429         }
32430         
32431         var formData = new FormData();
32432
32433         formData.append('returnHTML', 'NO');
32434         
32435         formData.append('crop', crop);
32436         
32437         if(typeof(file.filename) != 'undefined'){
32438             formData.append('filename', file.filename);
32439         }
32440         
32441         if(typeof(file.mimetype) != 'undefined'){
32442             formData.append('mimetype', file.mimetype);
32443         }
32444         
32445         Roo.log(formData);
32446         
32447         if(this.fireEvent('prepare', this, formData) != false){
32448             this.xhr.send(formData);
32449         };
32450     }
32451 });
32452
32453 /*
32454 * Licence: LGPL
32455 */
32456
32457 /**
32458  * @class Roo.bootstrap.DocumentViewer
32459  * @extends Roo.bootstrap.Component
32460  * Bootstrap DocumentViewer class
32461  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32462  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32463  * 
32464  * @constructor
32465  * Create a new DocumentViewer
32466  * @param {Object} config The config object
32467  */
32468
32469 Roo.bootstrap.DocumentViewer = function(config){
32470     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32471     
32472     this.addEvents({
32473         /**
32474          * @event initial
32475          * Fire after initEvent
32476          * @param {Roo.bootstrap.DocumentViewer} this
32477          */
32478         "initial" : true,
32479         /**
32480          * @event click
32481          * Fire after click
32482          * @param {Roo.bootstrap.DocumentViewer} this
32483          */
32484         "click" : true,
32485         /**
32486          * @event download
32487          * Fire after download button
32488          * @param {Roo.bootstrap.DocumentViewer} this
32489          */
32490         "download" : true,
32491         /**
32492          * @event trash
32493          * Fire after trash button
32494          * @param {Roo.bootstrap.DocumentViewer} this
32495          */
32496         "trash" : true
32497         
32498     });
32499 };
32500
32501 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32502     
32503     showDownload : true,
32504     
32505     showTrash : true,
32506     
32507     getAutoCreate : function()
32508     {
32509         var cfg = {
32510             tag : 'div',
32511             cls : 'roo-document-viewer',
32512             cn : [
32513                 {
32514                     tag : 'div',
32515                     cls : 'roo-document-viewer-body',
32516                     cn : [
32517                         {
32518                             tag : 'div',
32519                             cls : 'roo-document-viewer-thumb',
32520                             cn : [
32521                                 {
32522                                     tag : 'img',
32523                                     cls : 'roo-document-viewer-image'
32524                                 }
32525                             ]
32526                         }
32527                     ]
32528                 },
32529                 {
32530                     tag : 'div',
32531                     cls : 'roo-document-viewer-footer',
32532                     cn : {
32533                         tag : 'div',
32534                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32535                         cn : [
32536                             {
32537                                 tag : 'div',
32538                                 cls : 'btn-group roo-document-viewer-download',
32539                                 cn : [
32540                                     {
32541                                         tag : 'button',
32542                                         cls : 'btn btn-default',
32543                                         html : '<i class="fa fa-download"></i>'
32544                                     }
32545                                 ]
32546                             },
32547                             {
32548                                 tag : 'div',
32549                                 cls : 'btn-group roo-document-viewer-trash',
32550                                 cn : [
32551                                     {
32552                                         tag : 'button',
32553                                         cls : 'btn btn-default',
32554                                         html : '<i class="fa fa-trash"></i>'
32555                                     }
32556                                 ]
32557                             }
32558                         ]
32559                     }
32560                 }
32561             ]
32562         };
32563         
32564         return cfg;
32565     },
32566     
32567     initEvents : function()
32568     {
32569         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32570         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32571         
32572         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32573         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32574         
32575         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32576         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32577         
32578         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32579         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32580         
32581         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32582         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32583         
32584         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32585         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32586         
32587         this.bodyEl.on('click', this.onClick, this);
32588         this.downloadBtn.on('click', this.onDownload, this);
32589         this.trashBtn.on('click', this.onTrash, this);
32590         
32591         this.downloadBtn.hide();
32592         this.trashBtn.hide();
32593         
32594         if(this.showDownload){
32595             this.downloadBtn.show();
32596         }
32597         
32598         if(this.showTrash){
32599             this.trashBtn.show();
32600         }
32601         
32602         if(!this.showDownload && !this.showTrash) {
32603             this.footerEl.hide();
32604         }
32605         
32606     },
32607     
32608     initial : function()
32609     {
32610         this.fireEvent('initial', this);
32611         
32612     },
32613     
32614     onClick : function(e)
32615     {
32616         e.preventDefault();
32617         
32618         this.fireEvent('click', this);
32619     },
32620     
32621     onDownload : function(e)
32622     {
32623         e.preventDefault();
32624         
32625         this.fireEvent('download', this);
32626     },
32627     
32628     onTrash : function(e)
32629     {
32630         e.preventDefault();
32631         
32632         this.fireEvent('trash', this);
32633     }
32634     
32635 });
32636 /*
32637  * - LGPL
32638  *
32639  * nav progress bar
32640  * 
32641  */
32642
32643 /**
32644  * @class Roo.bootstrap.NavProgressBar
32645  * @extends Roo.bootstrap.Component
32646  * Bootstrap NavProgressBar class
32647  * 
32648  * @constructor
32649  * Create a new nav progress bar
32650  * @param {Object} config The config object
32651  */
32652
32653 Roo.bootstrap.NavProgressBar = function(config){
32654     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32655
32656     this.bullets = this.bullets || [];
32657    
32658 //    Roo.bootstrap.NavProgressBar.register(this);
32659      this.addEvents({
32660         /**
32661              * @event changed
32662              * Fires when the active item changes
32663              * @param {Roo.bootstrap.NavProgressBar} this
32664              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32665              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32666          */
32667         'changed': true
32668      });
32669     
32670 };
32671
32672 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32673     
32674     bullets : [],
32675     barItems : [],
32676     
32677     getAutoCreate : function()
32678     {
32679         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32680         
32681         cfg = {
32682             tag : 'div',
32683             cls : 'roo-navigation-bar-group',
32684             cn : [
32685                 {
32686                     tag : 'div',
32687                     cls : 'roo-navigation-top-bar'
32688                 },
32689                 {
32690                     tag : 'div',
32691                     cls : 'roo-navigation-bullets-bar',
32692                     cn : [
32693                         {
32694                             tag : 'ul',
32695                             cls : 'roo-navigation-bar'
32696                         }
32697                     ]
32698                 },
32699                 
32700                 {
32701                     tag : 'div',
32702                     cls : 'roo-navigation-bottom-bar'
32703                 }
32704             ]
32705             
32706         };
32707         
32708         return cfg;
32709         
32710     },
32711     
32712     initEvents: function() 
32713     {
32714         
32715     },
32716     
32717     onRender : function(ct, position) 
32718     {
32719         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32720         
32721         if(this.bullets.length){
32722             Roo.each(this.bullets, function(b){
32723                this.addItem(b);
32724             }, this);
32725         }
32726         
32727         this.format();
32728         
32729     },
32730     
32731     addItem : function(cfg)
32732     {
32733         var item = new Roo.bootstrap.NavProgressItem(cfg);
32734         
32735         item.parentId = this.id;
32736         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32737         
32738         if(cfg.html){
32739             var top = new Roo.bootstrap.Element({
32740                 tag : 'div',
32741                 cls : 'roo-navigation-bar-text'
32742             });
32743             
32744             var bottom = new Roo.bootstrap.Element({
32745                 tag : 'div',
32746                 cls : 'roo-navigation-bar-text'
32747             });
32748             
32749             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32750             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32751             
32752             var topText = new Roo.bootstrap.Element({
32753                 tag : 'span',
32754                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32755             });
32756             
32757             var bottomText = new Roo.bootstrap.Element({
32758                 tag : 'span',
32759                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32760             });
32761             
32762             topText.onRender(top.el, null);
32763             bottomText.onRender(bottom.el, null);
32764             
32765             item.topEl = top;
32766             item.bottomEl = bottom;
32767         }
32768         
32769         this.barItems.push(item);
32770         
32771         return item;
32772     },
32773     
32774     getActive : function()
32775     {
32776         var active = false;
32777         
32778         Roo.each(this.barItems, function(v){
32779             
32780             if (!v.isActive()) {
32781                 return;
32782             }
32783             
32784             active = v;
32785             return false;
32786             
32787         });
32788         
32789         return active;
32790     },
32791     
32792     setActiveItem : function(item)
32793     {
32794         var prev = false;
32795         
32796         Roo.each(this.barItems, function(v){
32797             if (v.rid == item.rid) {
32798                 return ;
32799             }
32800             
32801             if (v.isActive()) {
32802                 v.setActive(false);
32803                 prev = v;
32804             }
32805         });
32806
32807         item.setActive(true);
32808         
32809         this.fireEvent('changed', this, item, prev);
32810     },
32811     
32812     getBarItem: function(rid)
32813     {
32814         var ret = false;
32815         
32816         Roo.each(this.barItems, function(e) {
32817             if (e.rid != rid) {
32818                 return;
32819             }
32820             
32821             ret =  e;
32822             return false;
32823         });
32824         
32825         return ret;
32826     },
32827     
32828     indexOfItem : function(item)
32829     {
32830         var index = false;
32831         
32832         Roo.each(this.barItems, function(v, i){
32833             
32834             if (v.rid != item.rid) {
32835                 return;
32836             }
32837             
32838             index = i;
32839             return false
32840         });
32841         
32842         return index;
32843     },
32844     
32845     setActiveNext : function()
32846     {
32847         var i = this.indexOfItem(this.getActive());
32848         
32849         if (i > this.barItems.length) {
32850             return;
32851         }
32852         
32853         this.setActiveItem(this.barItems[i+1]);
32854     },
32855     
32856     setActivePrev : function()
32857     {
32858         var i = this.indexOfItem(this.getActive());
32859         
32860         if (i  < 1) {
32861             return;
32862         }
32863         
32864         this.setActiveItem(this.barItems[i-1]);
32865     },
32866     
32867     format : function()
32868     {
32869         if(!this.barItems.length){
32870             return;
32871         }
32872      
32873         var width = 100 / this.barItems.length;
32874         
32875         Roo.each(this.barItems, function(i){
32876             i.el.setStyle('width', width + '%');
32877             i.topEl.el.setStyle('width', width + '%');
32878             i.bottomEl.el.setStyle('width', width + '%');
32879         }, this);
32880         
32881     }
32882     
32883 });
32884 /*
32885  * - LGPL
32886  *
32887  * Nav Progress Item
32888  * 
32889  */
32890
32891 /**
32892  * @class Roo.bootstrap.NavProgressItem
32893  * @extends Roo.bootstrap.Component
32894  * Bootstrap NavProgressItem class
32895  * @cfg {String} rid the reference id
32896  * @cfg {Boolean} active (true|false) Is item active default false
32897  * @cfg {Boolean} disabled (true|false) Is item active default false
32898  * @cfg {String} html
32899  * @cfg {String} position (top|bottom) text position default bottom
32900  * @cfg {String} icon show icon instead of number
32901  * 
32902  * @constructor
32903  * Create a new NavProgressItem
32904  * @param {Object} config The config object
32905  */
32906 Roo.bootstrap.NavProgressItem = function(config){
32907     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32908     this.addEvents({
32909         // raw events
32910         /**
32911          * @event click
32912          * The raw click event for the entire grid.
32913          * @param {Roo.bootstrap.NavProgressItem} this
32914          * @param {Roo.EventObject} e
32915          */
32916         "click" : true
32917     });
32918    
32919 };
32920
32921 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32922     
32923     rid : '',
32924     active : false,
32925     disabled : false,
32926     html : '',
32927     position : 'bottom',
32928     icon : false,
32929     
32930     getAutoCreate : function()
32931     {
32932         var iconCls = 'roo-navigation-bar-item-icon';
32933         
32934         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32935         
32936         var cfg = {
32937             tag: 'li',
32938             cls: 'roo-navigation-bar-item',
32939             cn : [
32940                 {
32941                     tag : 'i',
32942                     cls : iconCls
32943                 }
32944             ]
32945         };
32946         
32947         if(this.active){
32948             cfg.cls += ' active';
32949         }
32950         if(this.disabled){
32951             cfg.cls += ' disabled';
32952         }
32953         
32954         return cfg;
32955     },
32956     
32957     disable : function()
32958     {
32959         this.setDisabled(true);
32960     },
32961     
32962     enable : function()
32963     {
32964         this.setDisabled(false);
32965     },
32966     
32967     initEvents: function() 
32968     {
32969         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32970         
32971         this.iconEl.on('click', this.onClick, this);
32972     },
32973     
32974     onClick : function(e)
32975     {
32976         e.preventDefault();
32977         
32978         if(this.disabled){
32979             return;
32980         }
32981         
32982         if(this.fireEvent('click', this, e) === false){
32983             return;
32984         };
32985         
32986         this.parent().setActiveItem(this);
32987     },
32988     
32989     isActive: function () 
32990     {
32991         return this.active;
32992     },
32993     
32994     setActive : function(state)
32995     {
32996         if(this.active == state){
32997             return;
32998         }
32999         
33000         this.active = state;
33001         
33002         if (state) {
33003             this.el.addClass('active');
33004             return;
33005         }
33006         
33007         this.el.removeClass('active');
33008         
33009         return;
33010     },
33011     
33012     setDisabled : function(state)
33013     {
33014         if(this.disabled == state){
33015             return;
33016         }
33017         
33018         this.disabled = state;
33019         
33020         if (state) {
33021             this.el.addClass('disabled');
33022             return;
33023         }
33024         
33025         this.el.removeClass('disabled');
33026     },
33027     
33028     tooltipEl : function()
33029     {
33030         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33031     }
33032 });
33033  
33034
33035  /*
33036  * - LGPL
33037  *
33038  * FieldLabel
33039  * 
33040  */
33041
33042 /**
33043  * @class Roo.bootstrap.FieldLabel
33044  * @extends Roo.bootstrap.Component
33045  * Bootstrap FieldLabel class
33046  * @cfg {String} html contents of the element
33047  * @cfg {String} tag tag of the element default label
33048  * @cfg {String} cls class of the element
33049  * @cfg {String} target label target 
33050  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33051  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33052  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33053  * @cfg {String} iconTooltip default "This field is required"
33054  * @cfg {String} indicatorpos (left|right) default left
33055  * 
33056  * @constructor
33057  * Create a new FieldLabel
33058  * @param {Object} config The config object
33059  */
33060
33061 Roo.bootstrap.FieldLabel = function(config){
33062     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33063     
33064     this.addEvents({
33065             /**
33066              * @event invalid
33067              * Fires after the field has been marked as invalid.
33068              * @param {Roo.form.FieldLabel} this
33069              * @param {String} msg The validation message
33070              */
33071             invalid : true,
33072             /**
33073              * @event valid
33074              * Fires after the field has been validated with no errors.
33075              * @param {Roo.form.FieldLabel} this
33076              */
33077             valid : true
33078         });
33079 };
33080
33081 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33082     
33083     tag: 'label',
33084     cls: '',
33085     html: '',
33086     target: '',
33087     allowBlank : true,
33088     invalidClass : 'has-warning',
33089     validClass : 'has-success',
33090     iconTooltip : 'This field is required',
33091     indicatorpos : 'left',
33092     
33093     getAutoCreate : function(){
33094         
33095         var cls = "";
33096         if (!this.allowBlank) {
33097             cls  = "visible";
33098         }
33099         
33100         var cfg = {
33101             tag : this.tag,
33102             cls : 'roo-bootstrap-field-label ' + this.cls,
33103             for : this.target,
33104             cn : [
33105                 {
33106                     tag : 'i',
33107                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33108                     tooltip : this.iconTooltip
33109                 },
33110                 {
33111                     tag : 'span',
33112                     html : this.html
33113                 }
33114             ] 
33115         };
33116         
33117         if(this.indicatorpos == 'right'){
33118             var cfg = {
33119                 tag : this.tag,
33120                 cls : 'roo-bootstrap-field-label ' + this.cls,
33121                 for : this.target,
33122                 cn : [
33123                     {
33124                         tag : 'span',
33125                         html : this.html
33126                     },
33127                     {
33128                         tag : 'i',
33129                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33130                         tooltip : this.iconTooltip
33131                     }
33132                 ] 
33133             };
33134         }
33135         
33136         return cfg;
33137     },
33138     
33139     initEvents: function() 
33140     {
33141         Roo.bootstrap.Element.superclass.initEvents.call(this);
33142         
33143         this.indicator = this.indicatorEl();
33144         
33145         if(this.indicator){
33146             this.indicator.removeClass('visible');
33147             this.indicator.addClass('invisible');
33148         }
33149         
33150         Roo.bootstrap.FieldLabel.register(this);
33151     },
33152     
33153     indicatorEl : function()
33154     {
33155         var indicator = this.el.select('i.roo-required-indicator',true).first();
33156         
33157         if(!indicator){
33158             return false;
33159         }
33160         
33161         return indicator;
33162         
33163     },
33164     
33165     /**
33166      * Mark this field as valid
33167      */
33168     markValid : function()
33169     {
33170         if(this.indicator){
33171             this.indicator.removeClass('visible');
33172             this.indicator.addClass('invisible');
33173         }
33174         if (Roo.bootstrap.version == 3) {
33175             this.el.removeClass(this.invalidClass);
33176             this.el.addClass(this.validClass);
33177         } else {
33178             this.el.removeClass('is-invalid');
33179             this.el.addClass('is-valid');
33180         }
33181         
33182         
33183         this.fireEvent('valid', this);
33184     },
33185     
33186     /**
33187      * Mark this field as invalid
33188      * @param {String} msg The validation message
33189      */
33190     markInvalid : function(msg)
33191     {
33192         if(this.indicator){
33193             this.indicator.removeClass('invisible');
33194             this.indicator.addClass('visible');
33195         }
33196           if (Roo.bootstrap.version == 3) {
33197             this.el.removeClass(this.validClass);
33198             this.el.addClass(this.invalidClass);
33199         } else {
33200             this.el.removeClass('is-valid');
33201             this.el.addClass('is-invalid');
33202         }
33203         
33204         
33205         this.fireEvent('invalid', this, msg);
33206     }
33207     
33208    
33209 });
33210
33211 Roo.apply(Roo.bootstrap.FieldLabel, {
33212     
33213     groups: {},
33214     
33215      /**
33216     * register a FieldLabel Group
33217     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33218     */
33219     register : function(label)
33220     {
33221         if(this.groups.hasOwnProperty(label.target)){
33222             return;
33223         }
33224      
33225         this.groups[label.target] = label;
33226         
33227     },
33228     /**
33229     * fetch a FieldLabel Group based on the target
33230     * @param {string} target
33231     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33232     */
33233     get: function(target) {
33234         if (typeof(this.groups[target]) == 'undefined') {
33235             return false;
33236         }
33237         
33238         return this.groups[target] ;
33239     }
33240 });
33241
33242  
33243
33244  /*
33245  * - LGPL
33246  *
33247  * page DateSplitField.
33248  * 
33249  */
33250
33251
33252 /**
33253  * @class Roo.bootstrap.DateSplitField
33254  * @extends Roo.bootstrap.Component
33255  * Bootstrap DateSplitField class
33256  * @cfg {string} fieldLabel - the label associated
33257  * @cfg {Number} labelWidth set the width of label (0-12)
33258  * @cfg {String} labelAlign (top|left)
33259  * @cfg {Boolean} dayAllowBlank (true|false) default false
33260  * @cfg {Boolean} monthAllowBlank (true|false) default false
33261  * @cfg {Boolean} yearAllowBlank (true|false) default false
33262  * @cfg {string} dayPlaceholder 
33263  * @cfg {string} monthPlaceholder
33264  * @cfg {string} yearPlaceholder
33265  * @cfg {string} dayFormat default 'd'
33266  * @cfg {string} monthFormat default 'm'
33267  * @cfg {string} yearFormat default 'Y'
33268  * @cfg {Number} labellg set the width of label (1-12)
33269  * @cfg {Number} labelmd set the width of label (1-12)
33270  * @cfg {Number} labelsm set the width of label (1-12)
33271  * @cfg {Number} labelxs set the width of label (1-12)
33272
33273  *     
33274  * @constructor
33275  * Create a new DateSplitField
33276  * @param {Object} config The config object
33277  */
33278
33279 Roo.bootstrap.DateSplitField = function(config){
33280     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33281     
33282     this.addEvents({
33283         // raw events
33284          /**
33285          * @event years
33286          * getting the data of years
33287          * @param {Roo.bootstrap.DateSplitField} this
33288          * @param {Object} years
33289          */
33290         "years" : true,
33291         /**
33292          * @event days
33293          * getting the data of days
33294          * @param {Roo.bootstrap.DateSplitField} this
33295          * @param {Object} days
33296          */
33297         "days" : true,
33298         /**
33299          * @event invalid
33300          * Fires after the field has been marked as invalid.
33301          * @param {Roo.form.Field} this
33302          * @param {String} msg The validation message
33303          */
33304         invalid : true,
33305        /**
33306          * @event valid
33307          * Fires after the field has been validated with no errors.
33308          * @param {Roo.form.Field} this
33309          */
33310         valid : true
33311     });
33312 };
33313
33314 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33315     
33316     fieldLabel : '',
33317     labelAlign : 'top',
33318     labelWidth : 3,
33319     dayAllowBlank : false,
33320     monthAllowBlank : false,
33321     yearAllowBlank : false,
33322     dayPlaceholder : '',
33323     monthPlaceholder : '',
33324     yearPlaceholder : '',
33325     dayFormat : 'd',
33326     monthFormat : 'm',
33327     yearFormat : 'Y',
33328     isFormField : true,
33329     labellg : 0,
33330     labelmd : 0,
33331     labelsm : 0,
33332     labelxs : 0,
33333     
33334     getAutoCreate : function()
33335     {
33336         var cfg = {
33337             tag : 'div',
33338             cls : 'row roo-date-split-field-group',
33339             cn : [
33340                 {
33341                     tag : 'input',
33342                     type : 'hidden',
33343                     cls : 'form-hidden-field roo-date-split-field-group-value',
33344                     name : this.name
33345                 }
33346             ]
33347         };
33348         
33349         var labelCls = 'col-md-12';
33350         var contentCls = 'col-md-4';
33351         
33352         if(this.fieldLabel){
33353             
33354             var label = {
33355                 tag : 'div',
33356                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33357                 cn : [
33358                     {
33359                         tag : 'label',
33360                         html : this.fieldLabel
33361                     }
33362                 ]
33363             };
33364             
33365             if(this.labelAlign == 'left'){
33366             
33367                 if(this.labelWidth > 12){
33368                     label.style = "width: " + this.labelWidth + 'px';
33369                 }
33370
33371                 if(this.labelWidth < 13 && this.labelmd == 0){
33372                     this.labelmd = this.labelWidth;
33373                 }
33374
33375                 if(this.labellg > 0){
33376                     labelCls = ' col-lg-' + this.labellg;
33377                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33378                 }
33379
33380                 if(this.labelmd > 0){
33381                     labelCls = ' col-md-' + this.labelmd;
33382                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33383                 }
33384
33385                 if(this.labelsm > 0){
33386                     labelCls = ' col-sm-' + this.labelsm;
33387                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33388                 }
33389
33390                 if(this.labelxs > 0){
33391                     labelCls = ' col-xs-' + this.labelxs;
33392                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33393                 }
33394             }
33395             
33396             label.cls += ' ' + labelCls;
33397             
33398             cfg.cn.push(label);
33399         }
33400         
33401         Roo.each(['day', 'month', 'year'], function(t){
33402             cfg.cn.push({
33403                 tag : 'div',
33404                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33405             });
33406         }, this);
33407         
33408         return cfg;
33409     },
33410     
33411     inputEl: function ()
33412     {
33413         return this.el.select('.roo-date-split-field-group-value', true).first();
33414     },
33415     
33416     onRender : function(ct, position) 
33417     {
33418         var _this = this;
33419         
33420         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33421         
33422         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33423         
33424         this.dayField = new Roo.bootstrap.ComboBox({
33425             allowBlank : this.dayAllowBlank,
33426             alwaysQuery : true,
33427             displayField : 'value',
33428             editable : false,
33429             fieldLabel : '',
33430             forceSelection : true,
33431             mode : 'local',
33432             placeholder : this.dayPlaceholder,
33433             selectOnFocus : true,
33434             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33435             triggerAction : 'all',
33436             typeAhead : true,
33437             valueField : 'value',
33438             store : new Roo.data.SimpleStore({
33439                 data : (function() {    
33440                     var days = [];
33441                     _this.fireEvent('days', _this, days);
33442                     return days;
33443                 })(),
33444                 fields : [ 'value' ]
33445             }),
33446             listeners : {
33447                 select : function (_self, record, index)
33448                 {
33449                     _this.setValue(_this.getValue());
33450                 }
33451             }
33452         });
33453
33454         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33455         
33456         this.monthField = new Roo.bootstrap.MonthField({
33457             after : '<i class=\"fa fa-calendar\"></i>',
33458             allowBlank : this.monthAllowBlank,
33459             placeholder : this.monthPlaceholder,
33460             readOnly : true,
33461             listeners : {
33462                 render : function (_self)
33463                 {
33464                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33465                         e.preventDefault();
33466                         _self.focus();
33467                     });
33468                 },
33469                 select : function (_self, oldvalue, newvalue)
33470                 {
33471                     _this.setValue(_this.getValue());
33472                 }
33473             }
33474         });
33475         
33476         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33477         
33478         this.yearField = new Roo.bootstrap.ComboBox({
33479             allowBlank : this.yearAllowBlank,
33480             alwaysQuery : true,
33481             displayField : 'value',
33482             editable : false,
33483             fieldLabel : '',
33484             forceSelection : true,
33485             mode : 'local',
33486             placeholder : this.yearPlaceholder,
33487             selectOnFocus : true,
33488             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33489             triggerAction : 'all',
33490             typeAhead : true,
33491             valueField : 'value',
33492             store : new Roo.data.SimpleStore({
33493                 data : (function() {
33494                     var years = [];
33495                     _this.fireEvent('years', _this, years);
33496                     return years;
33497                 })(),
33498                 fields : [ 'value' ]
33499             }),
33500             listeners : {
33501                 select : function (_self, record, index)
33502                 {
33503                     _this.setValue(_this.getValue());
33504                 }
33505             }
33506         });
33507
33508         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33509     },
33510     
33511     setValue : function(v, format)
33512     {
33513         this.inputEl.dom.value = v;
33514         
33515         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33516         
33517         var d = Date.parseDate(v, f);
33518         
33519         if(!d){
33520             this.validate();
33521             return;
33522         }
33523         
33524         this.setDay(d.format(this.dayFormat));
33525         this.setMonth(d.format(this.monthFormat));
33526         this.setYear(d.format(this.yearFormat));
33527         
33528         this.validate();
33529         
33530         return;
33531     },
33532     
33533     setDay : function(v)
33534     {
33535         this.dayField.setValue(v);
33536         this.inputEl.dom.value = this.getValue();
33537         this.validate();
33538         return;
33539     },
33540     
33541     setMonth : function(v)
33542     {
33543         this.monthField.setValue(v, true);
33544         this.inputEl.dom.value = this.getValue();
33545         this.validate();
33546         return;
33547     },
33548     
33549     setYear : function(v)
33550     {
33551         this.yearField.setValue(v);
33552         this.inputEl.dom.value = this.getValue();
33553         this.validate();
33554         return;
33555     },
33556     
33557     getDay : function()
33558     {
33559         return this.dayField.getValue();
33560     },
33561     
33562     getMonth : function()
33563     {
33564         return this.monthField.getValue();
33565     },
33566     
33567     getYear : function()
33568     {
33569         return this.yearField.getValue();
33570     },
33571     
33572     getValue : function()
33573     {
33574         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33575         
33576         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33577         
33578         return date;
33579     },
33580     
33581     reset : function()
33582     {
33583         this.setDay('');
33584         this.setMonth('');
33585         this.setYear('');
33586         this.inputEl.dom.value = '';
33587         this.validate();
33588         return;
33589     },
33590     
33591     validate : function()
33592     {
33593         var d = this.dayField.validate();
33594         var m = this.monthField.validate();
33595         var y = this.yearField.validate();
33596         
33597         var valid = true;
33598         
33599         if(
33600                 (!this.dayAllowBlank && !d) ||
33601                 (!this.monthAllowBlank && !m) ||
33602                 (!this.yearAllowBlank && !y)
33603         ){
33604             valid = false;
33605         }
33606         
33607         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33608             return valid;
33609         }
33610         
33611         if(valid){
33612             this.markValid();
33613             return valid;
33614         }
33615         
33616         this.markInvalid();
33617         
33618         return valid;
33619     },
33620     
33621     markValid : function()
33622     {
33623         
33624         var label = this.el.select('label', true).first();
33625         var icon = this.el.select('i.fa-star', true).first();
33626
33627         if(label && icon){
33628             icon.remove();
33629         }
33630         
33631         this.fireEvent('valid', this);
33632     },
33633     
33634      /**
33635      * Mark this field as invalid
33636      * @param {String} msg The validation message
33637      */
33638     markInvalid : function(msg)
33639     {
33640         
33641         var label = this.el.select('label', true).first();
33642         var icon = this.el.select('i.fa-star', true).first();
33643
33644         if(label && !icon){
33645             this.el.select('.roo-date-split-field-label', true).createChild({
33646                 tag : 'i',
33647                 cls : 'text-danger fa fa-lg fa-star',
33648                 tooltip : 'This field is required',
33649                 style : 'margin-right:5px;'
33650             }, label, true);
33651         }
33652         
33653         this.fireEvent('invalid', this, msg);
33654     },
33655     
33656     clearInvalid : function()
33657     {
33658         var label = this.el.select('label', true).first();
33659         var icon = this.el.select('i.fa-star', true).first();
33660
33661         if(label && icon){
33662             icon.remove();
33663         }
33664         
33665         this.fireEvent('valid', this);
33666     },
33667     
33668     getName: function()
33669     {
33670         return this.name;
33671     }
33672     
33673 });
33674
33675  /**
33676  *
33677  * This is based on 
33678  * http://masonry.desandro.com
33679  *
33680  * The idea is to render all the bricks based on vertical width...
33681  *
33682  * The original code extends 'outlayer' - we might need to use that....
33683  * 
33684  */
33685
33686
33687 /**
33688  * @class Roo.bootstrap.LayoutMasonry
33689  * @extends Roo.bootstrap.Component
33690  * Bootstrap Layout Masonry class
33691  * 
33692  * @constructor
33693  * Create a new Element
33694  * @param {Object} config The config object
33695  */
33696
33697 Roo.bootstrap.LayoutMasonry = function(config){
33698     
33699     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33700     
33701     this.bricks = [];
33702     
33703     Roo.bootstrap.LayoutMasonry.register(this);
33704     
33705     this.addEvents({
33706         // raw events
33707         /**
33708          * @event layout
33709          * Fire after layout the items
33710          * @param {Roo.bootstrap.LayoutMasonry} this
33711          * @param {Roo.EventObject} e
33712          */
33713         "layout" : true
33714     });
33715     
33716 };
33717
33718 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33719     
33720     /**
33721      * @cfg {Boolean} isLayoutInstant = no animation?
33722      */   
33723     isLayoutInstant : false, // needed?
33724    
33725     /**
33726      * @cfg {Number} boxWidth  width of the columns
33727      */   
33728     boxWidth : 450,
33729     
33730       /**
33731      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33732      */   
33733     boxHeight : 0,
33734     
33735     /**
33736      * @cfg {Number} padWidth padding below box..
33737      */   
33738     padWidth : 10, 
33739     
33740     /**
33741      * @cfg {Number} gutter gutter width..
33742      */   
33743     gutter : 10,
33744     
33745      /**
33746      * @cfg {Number} maxCols maximum number of columns
33747      */   
33748     
33749     maxCols: 0,
33750     
33751     /**
33752      * @cfg {Boolean} isAutoInitial defalut true
33753      */   
33754     isAutoInitial : true, 
33755     
33756     containerWidth: 0,
33757     
33758     /**
33759      * @cfg {Boolean} isHorizontal defalut false
33760      */   
33761     isHorizontal : false, 
33762
33763     currentSize : null,
33764     
33765     tag: 'div',
33766     
33767     cls: '',
33768     
33769     bricks: null, //CompositeElement
33770     
33771     cols : 1,
33772     
33773     _isLayoutInited : false,
33774     
33775 //    isAlternative : false, // only use for vertical layout...
33776     
33777     /**
33778      * @cfg {Number} alternativePadWidth padding below box..
33779      */   
33780     alternativePadWidth : 50,
33781     
33782     selectedBrick : [],
33783     
33784     getAutoCreate : function(){
33785         
33786         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33787         
33788         var cfg = {
33789             tag: this.tag,
33790             cls: 'blog-masonary-wrapper ' + this.cls,
33791             cn : {
33792                 cls : 'mas-boxes masonary'
33793             }
33794         };
33795         
33796         return cfg;
33797     },
33798     
33799     getChildContainer: function( )
33800     {
33801         if (this.boxesEl) {
33802             return this.boxesEl;
33803         }
33804         
33805         this.boxesEl = this.el.select('.mas-boxes').first();
33806         
33807         return this.boxesEl;
33808     },
33809     
33810     
33811     initEvents : function()
33812     {
33813         var _this = this;
33814         
33815         if(this.isAutoInitial){
33816             Roo.log('hook children rendered');
33817             this.on('childrenrendered', function() {
33818                 Roo.log('children rendered');
33819                 _this.initial();
33820             } ,this);
33821         }
33822     },
33823     
33824     initial : function()
33825     {
33826         this.selectedBrick = [];
33827         
33828         this.currentSize = this.el.getBox(true);
33829         
33830         Roo.EventManager.onWindowResize(this.resize, this); 
33831
33832         if(!this.isAutoInitial){
33833             this.layout();
33834             return;
33835         }
33836         
33837         this.layout();
33838         
33839         return;
33840         //this.layout.defer(500,this);
33841         
33842     },
33843     
33844     resize : function()
33845     {
33846         var cs = this.el.getBox(true);
33847         
33848         if (
33849                 this.currentSize.width == cs.width && 
33850                 this.currentSize.x == cs.x && 
33851                 this.currentSize.height == cs.height && 
33852                 this.currentSize.y == cs.y 
33853         ) {
33854             Roo.log("no change in with or X or Y");
33855             return;
33856         }
33857         
33858         this.currentSize = cs;
33859         
33860         this.layout();
33861         
33862     },
33863     
33864     layout : function()
33865     {   
33866         this._resetLayout();
33867         
33868         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33869         
33870         this.layoutItems( isInstant );
33871       
33872         this._isLayoutInited = true;
33873         
33874         this.fireEvent('layout', this);
33875         
33876     },
33877     
33878     _resetLayout : function()
33879     {
33880         if(this.isHorizontal){
33881             this.horizontalMeasureColumns();
33882             return;
33883         }
33884         
33885         this.verticalMeasureColumns();
33886         
33887     },
33888     
33889     verticalMeasureColumns : function()
33890     {
33891         this.getContainerWidth();
33892         
33893 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33894 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33895 //            return;
33896 //        }
33897         
33898         var boxWidth = this.boxWidth + this.padWidth;
33899         
33900         if(this.containerWidth < this.boxWidth){
33901             boxWidth = this.containerWidth
33902         }
33903         
33904         var containerWidth = this.containerWidth;
33905         
33906         var cols = Math.floor(containerWidth / boxWidth);
33907         
33908         this.cols = Math.max( cols, 1 );
33909         
33910         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33911         
33912         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33913         
33914         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33915         
33916         this.colWidth = boxWidth + avail - this.padWidth;
33917         
33918         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33919         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33920     },
33921     
33922     horizontalMeasureColumns : function()
33923     {
33924         this.getContainerWidth();
33925         
33926         var boxWidth = this.boxWidth;
33927         
33928         if(this.containerWidth < boxWidth){
33929             boxWidth = this.containerWidth;
33930         }
33931         
33932         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33933         
33934         this.el.setHeight(boxWidth);
33935         
33936     },
33937     
33938     getContainerWidth : function()
33939     {
33940         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33941     },
33942     
33943     layoutItems : function( isInstant )
33944     {
33945         Roo.log(this.bricks);
33946         
33947         var items = Roo.apply([], this.bricks);
33948         
33949         if(this.isHorizontal){
33950             this._horizontalLayoutItems( items , isInstant );
33951             return;
33952         }
33953         
33954 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33955 //            this._verticalAlternativeLayoutItems( items , isInstant );
33956 //            return;
33957 //        }
33958         
33959         this._verticalLayoutItems( items , isInstant );
33960         
33961     },
33962     
33963     _verticalLayoutItems : function ( items , isInstant)
33964     {
33965         if ( !items || !items.length ) {
33966             return;
33967         }
33968         
33969         var standard = [
33970             ['xs', 'xs', 'xs', 'tall'],
33971             ['xs', 'xs', 'tall'],
33972             ['xs', 'xs', 'sm'],
33973             ['xs', 'xs', 'xs'],
33974             ['xs', 'tall'],
33975             ['xs', 'sm'],
33976             ['xs', 'xs'],
33977             ['xs'],
33978             
33979             ['sm', 'xs', 'xs'],
33980             ['sm', 'xs'],
33981             ['sm'],
33982             
33983             ['tall', 'xs', 'xs', 'xs'],
33984             ['tall', 'xs', 'xs'],
33985             ['tall', 'xs'],
33986             ['tall']
33987             
33988         ];
33989         
33990         var queue = [];
33991         
33992         var boxes = [];
33993         
33994         var box = [];
33995         
33996         Roo.each(items, function(item, k){
33997             
33998             switch (item.size) {
33999                 // these layouts take up a full box,
34000                 case 'md' :
34001                 case 'md-left' :
34002                 case 'md-right' :
34003                 case 'wide' :
34004                     
34005                     if(box.length){
34006                         boxes.push(box);
34007                         box = [];
34008                     }
34009                     
34010                     boxes.push([item]);
34011                     
34012                     break;
34013                     
34014                 case 'xs' :
34015                 case 'sm' :
34016                 case 'tall' :
34017                     
34018                     box.push(item);
34019                     
34020                     break;
34021                 default :
34022                     break;
34023                     
34024             }
34025             
34026         }, this);
34027         
34028         if(box.length){
34029             boxes.push(box);
34030             box = [];
34031         }
34032         
34033         var filterPattern = function(box, length)
34034         {
34035             if(!box.length){
34036                 return;
34037             }
34038             
34039             var match = false;
34040             
34041             var pattern = box.slice(0, length);
34042             
34043             var format = [];
34044             
34045             Roo.each(pattern, function(i){
34046                 format.push(i.size);
34047             }, this);
34048             
34049             Roo.each(standard, function(s){
34050                 
34051                 if(String(s) != String(format)){
34052                     return;
34053                 }
34054                 
34055                 match = true;
34056                 return false;
34057                 
34058             }, this);
34059             
34060             if(!match && length == 1){
34061                 return;
34062             }
34063             
34064             if(!match){
34065                 filterPattern(box, length - 1);
34066                 return;
34067             }
34068                 
34069             queue.push(pattern);
34070
34071             box = box.slice(length, box.length);
34072
34073             filterPattern(box, 4);
34074
34075             return;
34076             
34077         }
34078         
34079         Roo.each(boxes, function(box, k){
34080             
34081             if(!box.length){
34082                 return;
34083             }
34084             
34085             if(box.length == 1){
34086                 queue.push(box);
34087                 return;
34088             }
34089             
34090             filterPattern(box, 4);
34091             
34092         }, this);
34093         
34094         this._processVerticalLayoutQueue( queue, isInstant );
34095         
34096     },
34097     
34098 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34099 //    {
34100 //        if ( !items || !items.length ) {
34101 //            return;
34102 //        }
34103 //
34104 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34105 //        
34106 //    },
34107     
34108     _horizontalLayoutItems : function ( items , isInstant)
34109     {
34110         if ( !items || !items.length || items.length < 3) {
34111             return;
34112         }
34113         
34114         items.reverse();
34115         
34116         var eItems = items.slice(0, 3);
34117         
34118         items = items.slice(3, items.length);
34119         
34120         var standard = [
34121             ['xs', 'xs', 'xs', 'wide'],
34122             ['xs', 'xs', 'wide'],
34123             ['xs', 'xs', 'sm'],
34124             ['xs', 'xs', 'xs'],
34125             ['xs', 'wide'],
34126             ['xs', 'sm'],
34127             ['xs', 'xs'],
34128             ['xs'],
34129             
34130             ['sm', 'xs', 'xs'],
34131             ['sm', 'xs'],
34132             ['sm'],
34133             
34134             ['wide', 'xs', 'xs', 'xs'],
34135             ['wide', 'xs', 'xs'],
34136             ['wide', 'xs'],
34137             ['wide'],
34138             
34139             ['wide-thin']
34140         ];
34141         
34142         var queue = [];
34143         
34144         var boxes = [];
34145         
34146         var box = [];
34147         
34148         Roo.each(items, function(item, k){
34149             
34150             switch (item.size) {
34151                 case 'md' :
34152                 case 'md-left' :
34153                 case 'md-right' :
34154                 case 'tall' :
34155                     
34156                     if(box.length){
34157                         boxes.push(box);
34158                         box = [];
34159                     }
34160                     
34161                     boxes.push([item]);
34162                     
34163                     break;
34164                     
34165                 case 'xs' :
34166                 case 'sm' :
34167                 case 'wide' :
34168                 case 'wide-thin' :
34169                     
34170                     box.push(item);
34171                     
34172                     break;
34173                 default :
34174                     break;
34175                     
34176             }
34177             
34178         }, this);
34179         
34180         if(box.length){
34181             boxes.push(box);
34182             box = [];
34183         }
34184         
34185         var filterPattern = function(box, length)
34186         {
34187             if(!box.length){
34188                 return;
34189             }
34190             
34191             var match = false;
34192             
34193             var pattern = box.slice(0, length);
34194             
34195             var format = [];
34196             
34197             Roo.each(pattern, function(i){
34198                 format.push(i.size);
34199             }, this);
34200             
34201             Roo.each(standard, function(s){
34202                 
34203                 if(String(s) != String(format)){
34204                     return;
34205                 }
34206                 
34207                 match = true;
34208                 return false;
34209                 
34210             }, this);
34211             
34212             if(!match && length == 1){
34213                 return;
34214             }
34215             
34216             if(!match){
34217                 filterPattern(box, length - 1);
34218                 return;
34219             }
34220                 
34221             queue.push(pattern);
34222
34223             box = box.slice(length, box.length);
34224
34225             filterPattern(box, 4);
34226
34227             return;
34228             
34229         }
34230         
34231         Roo.each(boxes, function(box, k){
34232             
34233             if(!box.length){
34234                 return;
34235             }
34236             
34237             if(box.length == 1){
34238                 queue.push(box);
34239                 return;
34240             }
34241             
34242             filterPattern(box, 4);
34243             
34244         }, this);
34245         
34246         
34247         var prune = [];
34248         
34249         var pos = this.el.getBox(true);
34250         
34251         var minX = pos.x;
34252         
34253         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34254         
34255         var hit_end = false;
34256         
34257         Roo.each(queue, function(box){
34258             
34259             if(hit_end){
34260                 
34261                 Roo.each(box, function(b){
34262                 
34263                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34264                     b.el.hide();
34265
34266                 }, this);
34267
34268                 return;
34269             }
34270             
34271             var mx = 0;
34272             
34273             Roo.each(box, function(b){
34274                 
34275                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34276                 b.el.show();
34277
34278                 mx = Math.max(mx, b.x);
34279                 
34280             }, this);
34281             
34282             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34283             
34284             if(maxX < minX){
34285                 
34286                 Roo.each(box, function(b){
34287                 
34288                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34289                     b.el.hide();
34290                     
34291                 }, this);
34292                 
34293                 hit_end = true;
34294                 
34295                 return;
34296             }
34297             
34298             prune.push(box);
34299             
34300         }, this);
34301         
34302         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34303     },
34304     
34305     /** Sets position of item in DOM
34306     * @param {Element} item
34307     * @param {Number} x - horizontal position
34308     * @param {Number} y - vertical position
34309     * @param {Boolean} isInstant - disables transitions
34310     */
34311     _processVerticalLayoutQueue : function( queue, isInstant )
34312     {
34313         var pos = this.el.getBox(true);
34314         var x = pos.x;
34315         var y = pos.y;
34316         var maxY = [];
34317         
34318         for (var i = 0; i < this.cols; i++){
34319             maxY[i] = pos.y;
34320         }
34321         
34322         Roo.each(queue, function(box, k){
34323             
34324             var col = k % this.cols;
34325             
34326             Roo.each(box, function(b,kk){
34327                 
34328                 b.el.position('absolute');
34329                 
34330                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34331                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34332                 
34333                 if(b.size == 'md-left' || b.size == 'md-right'){
34334                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34335                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34336                 }
34337                 
34338                 b.el.setWidth(width);
34339                 b.el.setHeight(height);
34340                 // iframe?
34341                 b.el.select('iframe',true).setSize(width,height);
34342                 
34343             }, this);
34344             
34345             for (var i = 0; i < this.cols; i++){
34346                 
34347                 if(maxY[i] < maxY[col]){
34348                     col = i;
34349                     continue;
34350                 }
34351                 
34352                 col = Math.min(col, i);
34353                 
34354             }
34355             
34356             x = pos.x + col * (this.colWidth + this.padWidth);
34357             
34358             y = maxY[col];
34359             
34360             var positions = [];
34361             
34362             switch (box.length){
34363                 case 1 :
34364                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34365                     break;
34366                 case 2 :
34367                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34368                     break;
34369                 case 3 :
34370                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34371                     break;
34372                 case 4 :
34373                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34374                     break;
34375                 default :
34376                     break;
34377             }
34378             
34379             Roo.each(box, function(b,kk){
34380                 
34381                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34382                 
34383                 var sz = b.el.getSize();
34384                 
34385                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34386                 
34387             }, this);
34388             
34389         }, this);
34390         
34391         var mY = 0;
34392         
34393         for (var i = 0; i < this.cols; i++){
34394             mY = Math.max(mY, maxY[i]);
34395         }
34396         
34397         this.el.setHeight(mY - pos.y);
34398         
34399     },
34400     
34401 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34402 //    {
34403 //        var pos = this.el.getBox(true);
34404 //        var x = pos.x;
34405 //        var y = pos.y;
34406 //        var maxX = pos.right;
34407 //        
34408 //        var maxHeight = 0;
34409 //        
34410 //        Roo.each(items, function(item, k){
34411 //            
34412 //            var c = k % 2;
34413 //            
34414 //            item.el.position('absolute');
34415 //                
34416 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34417 //
34418 //            item.el.setWidth(width);
34419 //
34420 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34421 //
34422 //            item.el.setHeight(height);
34423 //            
34424 //            if(c == 0){
34425 //                item.el.setXY([x, y], isInstant ? false : true);
34426 //            } else {
34427 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34428 //            }
34429 //            
34430 //            y = y + height + this.alternativePadWidth;
34431 //            
34432 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34433 //            
34434 //        }, this);
34435 //        
34436 //        this.el.setHeight(maxHeight);
34437 //        
34438 //    },
34439     
34440     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34441     {
34442         var pos = this.el.getBox(true);
34443         
34444         var minX = pos.x;
34445         var minY = pos.y;
34446         
34447         var maxX = pos.right;
34448         
34449         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34450         
34451         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34452         
34453         Roo.each(queue, function(box, k){
34454             
34455             Roo.each(box, function(b, kk){
34456                 
34457                 b.el.position('absolute');
34458                 
34459                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34460                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34461                 
34462                 if(b.size == 'md-left' || b.size == 'md-right'){
34463                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34464                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34465                 }
34466                 
34467                 b.el.setWidth(width);
34468                 b.el.setHeight(height);
34469                 
34470             }, this);
34471             
34472             if(!box.length){
34473                 return;
34474             }
34475             
34476             var positions = [];
34477             
34478             switch (box.length){
34479                 case 1 :
34480                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34481                     break;
34482                 case 2 :
34483                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34484                     break;
34485                 case 3 :
34486                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34487                     break;
34488                 case 4 :
34489                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34490                     break;
34491                 default :
34492                     break;
34493             }
34494             
34495             Roo.each(box, function(b,kk){
34496                 
34497                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34498                 
34499                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34500                 
34501             }, this);
34502             
34503         }, this);
34504         
34505     },
34506     
34507     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34508     {
34509         Roo.each(eItems, function(b,k){
34510             
34511             b.size = (k == 0) ? 'sm' : 'xs';
34512             b.x = (k == 0) ? 2 : 1;
34513             b.y = (k == 0) ? 2 : 1;
34514             
34515             b.el.position('absolute');
34516             
34517             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34518                 
34519             b.el.setWidth(width);
34520             
34521             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34522             
34523             b.el.setHeight(height);
34524             
34525         }, this);
34526
34527         var positions = [];
34528         
34529         positions.push({
34530             x : maxX - this.unitWidth * 2 - this.gutter,
34531             y : minY
34532         });
34533         
34534         positions.push({
34535             x : maxX - this.unitWidth,
34536             y : minY + (this.unitWidth + this.gutter) * 2
34537         });
34538         
34539         positions.push({
34540             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34541             y : minY
34542         });
34543         
34544         Roo.each(eItems, function(b,k){
34545             
34546             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34547
34548         }, this);
34549         
34550     },
34551     
34552     getVerticalOneBoxColPositions : function(x, y, box)
34553     {
34554         var pos = [];
34555         
34556         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34557         
34558         if(box[0].size == 'md-left'){
34559             rand = 0;
34560         }
34561         
34562         if(box[0].size == 'md-right'){
34563             rand = 1;
34564         }
34565         
34566         pos.push({
34567             x : x + (this.unitWidth + this.gutter) * rand,
34568             y : y
34569         });
34570         
34571         return pos;
34572     },
34573     
34574     getVerticalTwoBoxColPositions : function(x, y, box)
34575     {
34576         var pos = [];
34577         
34578         if(box[0].size == 'xs'){
34579             
34580             pos.push({
34581                 x : x,
34582                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34583             });
34584
34585             pos.push({
34586                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34587                 y : y
34588             });
34589             
34590             return pos;
34591             
34592         }
34593         
34594         pos.push({
34595             x : x,
34596             y : y
34597         });
34598
34599         pos.push({
34600             x : x + (this.unitWidth + this.gutter) * 2,
34601             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34602         });
34603         
34604         return pos;
34605         
34606     },
34607     
34608     getVerticalThreeBoxColPositions : function(x, y, box)
34609     {
34610         var pos = [];
34611         
34612         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34613             
34614             pos.push({
34615                 x : x,
34616                 y : y
34617             });
34618
34619             pos.push({
34620                 x : x + (this.unitWidth + this.gutter) * 1,
34621                 y : y
34622             });
34623             
34624             pos.push({
34625                 x : x + (this.unitWidth + this.gutter) * 2,
34626                 y : y
34627             });
34628             
34629             return pos;
34630             
34631         }
34632         
34633         if(box[0].size == 'xs' && box[1].size == 'xs'){
34634             
34635             pos.push({
34636                 x : x,
34637                 y : y
34638             });
34639
34640             pos.push({
34641                 x : x,
34642                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34643             });
34644             
34645             pos.push({
34646                 x : x + (this.unitWidth + this.gutter) * 1,
34647                 y : y
34648             });
34649             
34650             return pos;
34651             
34652         }
34653         
34654         pos.push({
34655             x : x,
34656             y : y
34657         });
34658
34659         pos.push({
34660             x : x + (this.unitWidth + this.gutter) * 2,
34661             y : y
34662         });
34663
34664         pos.push({
34665             x : x + (this.unitWidth + this.gutter) * 2,
34666             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34667         });
34668             
34669         return pos;
34670         
34671     },
34672     
34673     getVerticalFourBoxColPositions : function(x, y, box)
34674     {
34675         var pos = [];
34676         
34677         if(box[0].size == 'xs'){
34678             
34679             pos.push({
34680                 x : x,
34681                 y : y
34682             });
34683
34684             pos.push({
34685                 x : x,
34686                 y : y + (this.unitHeight + this.gutter) * 1
34687             });
34688             
34689             pos.push({
34690                 x : x,
34691                 y : y + (this.unitHeight + this.gutter) * 2
34692             });
34693             
34694             pos.push({
34695                 x : x + (this.unitWidth + this.gutter) * 1,
34696                 y : y
34697             });
34698             
34699             return pos;
34700             
34701         }
34702         
34703         pos.push({
34704             x : x,
34705             y : y
34706         });
34707
34708         pos.push({
34709             x : x + (this.unitWidth + this.gutter) * 2,
34710             y : y
34711         });
34712
34713         pos.push({
34714             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34715             y : y + (this.unitHeight + this.gutter) * 1
34716         });
34717
34718         pos.push({
34719             x : x + (this.unitWidth + this.gutter) * 2,
34720             y : y + (this.unitWidth + this.gutter) * 2
34721         });
34722
34723         return pos;
34724         
34725     },
34726     
34727     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34728     {
34729         var pos = [];
34730         
34731         if(box[0].size == 'md-left'){
34732             pos.push({
34733                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34734                 y : minY
34735             });
34736             
34737             return pos;
34738         }
34739         
34740         if(box[0].size == 'md-right'){
34741             pos.push({
34742                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34743                 y : minY + (this.unitWidth + this.gutter) * 1
34744             });
34745             
34746             return pos;
34747         }
34748         
34749         var rand = Math.floor(Math.random() * (4 - box[0].y));
34750         
34751         pos.push({
34752             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34753             y : minY + (this.unitWidth + this.gutter) * rand
34754         });
34755         
34756         return pos;
34757         
34758     },
34759     
34760     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34761     {
34762         var pos = [];
34763         
34764         if(box[0].size == 'xs'){
34765             
34766             pos.push({
34767                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34768                 y : minY
34769             });
34770
34771             pos.push({
34772                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34773                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34774             });
34775             
34776             return pos;
34777             
34778         }
34779         
34780         pos.push({
34781             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34782             y : minY
34783         });
34784
34785         pos.push({
34786             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34787             y : minY + (this.unitWidth + this.gutter) * 2
34788         });
34789         
34790         return pos;
34791         
34792     },
34793     
34794     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34795     {
34796         var pos = [];
34797         
34798         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34799             
34800             pos.push({
34801                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34802                 y : minY
34803             });
34804
34805             pos.push({
34806                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34807                 y : minY + (this.unitWidth + this.gutter) * 1
34808             });
34809             
34810             pos.push({
34811                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34812                 y : minY + (this.unitWidth + this.gutter) * 2
34813             });
34814             
34815             return pos;
34816             
34817         }
34818         
34819         if(box[0].size == 'xs' && box[1].size == 'xs'){
34820             
34821             pos.push({
34822                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34823                 y : minY
34824             });
34825
34826             pos.push({
34827                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34828                 y : minY
34829             });
34830             
34831             pos.push({
34832                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34833                 y : minY + (this.unitWidth + this.gutter) * 1
34834             });
34835             
34836             return pos;
34837             
34838         }
34839         
34840         pos.push({
34841             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34842             y : minY
34843         });
34844
34845         pos.push({
34846             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34847             y : minY + (this.unitWidth + this.gutter) * 2
34848         });
34849
34850         pos.push({
34851             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34852             y : minY + (this.unitWidth + this.gutter) * 2
34853         });
34854             
34855         return pos;
34856         
34857     },
34858     
34859     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34860     {
34861         var pos = [];
34862         
34863         if(box[0].size == 'xs'){
34864             
34865             pos.push({
34866                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34867                 y : minY
34868             });
34869
34870             pos.push({
34871                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34872                 y : minY
34873             });
34874             
34875             pos.push({
34876                 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),
34877                 y : minY
34878             });
34879             
34880             pos.push({
34881                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34882                 y : minY + (this.unitWidth + this.gutter) * 1
34883             });
34884             
34885             return pos;
34886             
34887         }
34888         
34889         pos.push({
34890             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34891             y : minY
34892         });
34893         
34894         pos.push({
34895             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34896             y : minY + (this.unitWidth + this.gutter) * 2
34897         });
34898         
34899         pos.push({
34900             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34901             y : minY + (this.unitWidth + this.gutter) * 2
34902         });
34903         
34904         pos.push({
34905             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),
34906             y : minY + (this.unitWidth + this.gutter) * 2
34907         });
34908
34909         return pos;
34910         
34911     },
34912     
34913     /**
34914     * remove a Masonry Brick
34915     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34916     */
34917     removeBrick : function(brick_id)
34918     {
34919         if (!brick_id) {
34920             return;
34921         }
34922         
34923         for (var i = 0; i<this.bricks.length; i++) {
34924             if (this.bricks[i].id == brick_id) {
34925                 this.bricks.splice(i,1);
34926                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34927                 this.initial();
34928             }
34929         }
34930     },
34931     
34932     /**
34933     * adds a Masonry Brick
34934     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34935     */
34936     addBrick : function(cfg)
34937     {
34938         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34939         //this.register(cn);
34940         cn.parentId = this.id;
34941         cn.render(this.el);
34942         return cn;
34943     },
34944     
34945     /**
34946     * register a Masonry Brick
34947     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34948     */
34949     
34950     register : function(brick)
34951     {
34952         this.bricks.push(brick);
34953         brick.masonryId = this.id;
34954     },
34955     
34956     /**
34957     * clear all the Masonry Brick
34958     */
34959     clearAll : function()
34960     {
34961         this.bricks = [];
34962         //this.getChildContainer().dom.innerHTML = "";
34963         this.el.dom.innerHTML = '';
34964     },
34965     
34966     getSelected : function()
34967     {
34968         if (!this.selectedBrick) {
34969             return false;
34970         }
34971         
34972         return this.selectedBrick;
34973     }
34974 });
34975
34976 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34977     
34978     groups: {},
34979      /**
34980     * register a Masonry Layout
34981     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34982     */
34983     
34984     register : function(layout)
34985     {
34986         this.groups[layout.id] = layout;
34987     },
34988     /**
34989     * fetch a  Masonry Layout based on the masonry layout ID
34990     * @param {string} the masonry layout to add
34991     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34992     */
34993     
34994     get: function(layout_id) {
34995         if (typeof(this.groups[layout_id]) == 'undefined') {
34996             return false;
34997         }
34998         return this.groups[layout_id] ;
34999     }
35000     
35001     
35002     
35003 });
35004
35005  
35006
35007  /**
35008  *
35009  * This is based on 
35010  * http://masonry.desandro.com
35011  *
35012  * The idea is to render all the bricks based on vertical width...
35013  *
35014  * The original code extends 'outlayer' - we might need to use that....
35015  * 
35016  */
35017
35018
35019 /**
35020  * @class Roo.bootstrap.LayoutMasonryAuto
35021  * @extends Roo.bootstrap.Component
35022  * Bootstrap Layout Masonry class
35023  * 
35024  * @constructor
35025  * Create a new Element
35026  * @param {Object} config The config object
35027  */
35028
35029 Roo.bootstrap.LayoutMasonryAuto = function(config){
35030     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35031 };
35032
35033 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35034     
35035       /**
35036      * @cfg {Boolean} isFitWidth  - resize the width..
35037      */   
35038     isFitWidth : false,  // options..
35039     /**
35040      * @cfg {Boolean} isOriginLeft = left align?
35041      */   
35042     isOriginLeft : true,
35043     /**
35044      * @cfg {Boolean} isOriginTop = top align?
35045      */   
35046     isOriginTop : false,
35047     /**
35048      * @cfg {Boolean} isLayoutInstant = no animation?
35049      */   
35050     isLayoutInstant : false, // needed?
35051     /**
35052      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35053      */   
35054     isResizingContainer : true,
35055     /**
35056      * @cfg {Number} columnWidth  width of the columns 
35057      */   
35058     
35059     columnWidth : 0,
35060     
35061     /**
35062      * @cfg {Number} maxCols maximum number of columns
35063      */   
35064     
35065     maxCols: 0,
35066     /**
35067      * @cfg {Number} padHeight padding below box..
35068      */   
35069     
35070     padHeight : 10, 
35071     
35072     /**
35073      * @cfg {Boolean} isAutoInitial defalut true
35074      */   
35075     
35076     isAutoInitial : true, 
35077     
35078     // private?
35079     gutter : 0,
35080     
35081     containerWidth: 0,
35082     initialColumnWidth : 0,
35083     currentSize : null,
35084     
35085     colYs : null, // array.
35086     maxY : 0,
35087     padWidth: 10,
35088     
35089     
35090     tag: 'div',
35091     cls: '',
35092     bricks: null, //CompositeElement
35093     cols : 0, // array?
35094     // element : null, // wrapped now this.el
35095     _isLayoutInited : null, 
35096     
35097     
35098     getAutoCreate : function(){
35099         
35100         var cfg = {
35101             tag: this.tag,
35102             cls: 'blog-masonary-wrapper ' + this.cls,
35103             cn : {
35104                 cls : 'mas-boxes masonary'
35105             }
35106         };
35107         
35108         return cfg;
35109     },
35110     
35111     getChildContainer: function( )
35112     {
35113         if (this.boxesEl) {
35114             return this.boxesEl;
35115         }
35116         
35117         this.boxesEl = this.el.select('.mas-boxes').first();
35118         
35119         return this.boxesEl;
35120     },
35121     
35122     
35123     initEvents : function()
35124     {
35125         var _this = this;
35126         
35127         if(this.isAutoInitial){
35128             Roo.log('hook children rendered');
35129             this.on('childrenrendered', function() {
35130                 Roo.log('children rendered');
35131                 _this.initial();
35132             } ,this);
35133         }
35134         
35135     },
35136     
35137     initial : function()
35138     {
35139         this.reloadItems();
35140
35141         this.currentSize = this.el.getBox(true);
35142
35143         /// was window resize... - let's see if this works..
35144         Roo.EventManager.onWindowResize(this.resize, this); 
35145
35146         if(!this.isAutoInitial){
35147             this.layout();
35148             return;
35149         }
35150         
35151         this.layout.defer(500,this);
35152     },
35153     
35154     reloadItems: function()
35155     {
35156         this.bricks = this.el.select('.masonry-brick', true);
35157         
35158         this.bricks.each(function(b) {
35159             //Roo.log(b.getSize());
35160             if (!b.attr('originalwidth')) {
35161                 b.attr('originalwidth',  b.getSize().width);
35162             }
35163             
35164         });
35165         
35166         Roo.log(this.bricks.elements.length);
35167     },
35168     
35169     resize : function()
35170     {
35171         Roo.log('resize');
35172         var cs = this.el.getBox(true);
35173         
35174         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35175             Roo.log("no change in with or X");
35176             return;
35177         }
35178         this.currentSize = cs;
35179         this.layout();
35180     },
35181     
35182     layout : function()
35183     {
35184          Roo.log('layout');
35185         this._resetLayout();
35186         //this._manageStamps();
35187       
35188         // don't animate first layout
35189         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35190         this.layoutItems( isInstant );
35191       
35192         // flag for initalized
35193         this._isLayoutInited = true;
35194     },
35195     
35196     layoutItems : function( isInstant )
35197     {
35198         //var items = this._getItemsForLayout( this.items );
35199         // original code supports filtering layout items.. we just ignore it..
35200         
35201         this._layoutItems( this.bricks , isInstant );
35202       
35203         this._postLayout();
35204     },
35205     _layoutItems : function ( items , isInstant)
35206     {
35207        //this.fireEvent( 'layout', this, items );
35208     
35209
35210         if ( !items || !items.elements.length ) {
35211           // no items, emit event with empty array
35212             return;
35213         }
35214
35215         var queue = [];
35216         items.each(function(item) {
35217             Roo.log("layout item");
35218             Roo.log(item);
35219             // get x/y object from method
35220             var position = this._getItemLayoutPosition( item );
35221             // enqueue
35222             position.item = item;
35223             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35224             queue.push( position );
35225         }, this);
35226       
35227         this._processLayoutQueue( queue );
35228     },
35229     /** Sets position of item in DOM
35230     * @param {Element} item
35231     * @param {Number} x - horizontal position
35232     * @param {Number} y - vertical position
35233     * @param {Boolean} isInstant - disables transitions
35234     */
35235     _processLayoutQueue : function( queue )
35236     {
35237         for ( var i=0, len = queue.length; i < len; i++ ) {
35238             var obj = queue[i];
35239             obj.item.position('absolute');
35240             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35241         }
35242     },
35243       
35244     
35245     /**
35246     * Any logic you want to do after each layout,
35247     * i.e. size the container
35248     */
35249     _postLayout : function()
35250     {
35251         this.resizeContainer();
35252     },
35253     
35254     resizeContainer : function()
35255     {
35256         if ( !this.isResizingContainer ) {
35257             return;
35258         }
35259         var size = this._getContainerSize();
35260         if ( size ) {
35261             this.el.setSize(size.width,size.height);
35262             this.boxesEl.setSize(size.width,size.height);
35263         }
35264     },
35265     
35266     
35267     
35268     _resetLayout : function()
35269     {
35270         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35271         this.colWidth = this.el.getWidth();
35272         //this.gutter = this.el.getWidth(); 
35273         
35274         this.measureColumns();
35275
35276         // reset column Y
35277         var i = this.cols;
35278         this.colYs = [];
35279         while (i--) {
35280             this.colYs.push( 0 );
35281         }
35282     
35283         this.maxY = 0;
35284     },
35285
35286     measureColumns : function()
35287     {
35288         this.getContainerWidth();
35289       // if columnWidth is 0, default to outerWidth of first item
35290         if ( !this.columnWidth ) {
35291             var firstItem = this.bricks.first();
35292             Roo.log(firstItem);
35293             this.columnWidth  = this.containerWidth;
35294             if (firstItem && firstItem.attr('originalwidth') ) {
35295                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35296             }
35297             // columnWidth fall back to item of first element
35298             Roo.log("set column width?");
35299                         this.initialColumnWidth = this.columnWidth  ;
35300
35301             // if first elem has no width, default to size of container
35302             
35303         }
35304         
35305         
35306         if (this.initialColumnWidth) {
35307             this.columnWidth = this.initialColumnWidth;
35308         }
35309         
35310         
35311             
35312         // column width is fixed at the top - however if container width get's smaller we should
35313         // reduce it...
35314         
35315         // this bit calcs how man columns..
35316             
35317         var columnWidth = this.columnWidth += this.gutter;
35318       
35319         // calculate columns
35320         var containerWidth = this.containerWidth + this.gutter;
35321         
35322         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35323         // fix rounding errors, typically with gutters
35324         var excess = columnWidth - containerWidth % columnWidth;
35325         
35326         
35327         // if overshoot is less than a pixel, round up, otherwise floor it
35328         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35329         cols = Math[ mathMethod ]( cols );
35330         this.cols = Math.max( cols, 1 );
35331         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35332         
35333          // padding positioning..
35334         var totalColWidth = this.cols * this.columnWidth;
35335         var padavail = this.containerWidth - totalColWidth;
35336         // so for 2 columns - we need 3 'pads'
35337         
35338         var padNeeded = (1+this.cols) * this.padWidth;
35339         
35340         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35341         
35342         this.columnWidth += padExtra
35343         //this.padWidth = Math.floor(padavail /  ( this.cols));
35344         
35345         // adjust colum width so that padding is fixed??
35346         
35347         // we have 3 columns ... total = width * 3
35348         // we have X left over... that should be used by 
35349         
35350         //if (this.expandC) {
35351             
35352         //}
35353         
35354         
35355         
35356     },
35357     
35358     getContainerWidth : function()
35359     {
35360        /* // container is parent if fit width
35361         var container = this.isFitWidth ? this.element.parentNode : this.element;
35362         // check that this.size and size are there
35363         // IE8 triggers resize on body size change, so they might not be
35364         
35365         var size = getSize( container );  //FIXME
35366         this.containerWidth = size && size.innerWidth; //FIXME
35367         */
35368          
35369         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35370         
35371     },
35372     
35373     _getItemLayoutPosition : function( item )  // what is item?
35374     {
35375         // we resize the item to our columnWidth..
35376       
35377         item.setWidth(this.columnWidth);
35378         item.autoBoxAdjust  = false;
35379         
35380         var sz = item.getSize();
35381  
35382         // how many columns does this brick span
35383         var remainder = this.containerWidth % this.columnWidth;
35384         
35385         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35386         // round if off by 1 pixel, otherwise use ceil
35387         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35388         colSpan = Math.min( colSpan, this.cols );
35389         
35390         // normally this should be '1' as we dont' currently allow multi width columns..
35391         
35392         var colGroup = this._getColGroup( colSpan );
35393         // get the minimum Y value from the columns
35394         var minimumY = Math.min.apply( Math, colGroup );
35395         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35396         
35397         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35398          
35399         // position the brick
35400         var position = {
35401             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35402             y: this.currentSize.y + minimumY + this.padHeight
35403         };
35404         
35405         Roo.log(position);
35406         // apply setHeight to necessary columns
35407         var setHeight = minimumY + sz.height + this.padHeight;
35408         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35409         
35410         var setSpan = this.cols + 1 - colGroup.length;
35411         for ( var i = 0; i < setSpan; i++ ) {
35412           this.colYs[ shortColIndex + i ] = setHeight ;
35413         }
35414       
35415         return position;
35416     },
35417     
35418     /**
35419      * @param {Number} colSpan - number of columns the element spans
35420      * @returns {Array} colGroup
35421      */
35422     _getColGroup : function( colSpan )
35423     {
35424         if ( colSpan < 2 ) {
35425           // if brick spans only one column, use all the column Ys
35426           return this.colYs;
35427         }
35428       
35429         var colGroup = [];
35430         // how many different places could this brick fit horizontally
35431         var groupCount = this.cols + 1 - colSpan;
35432         // for each group potential horizontal position
35433         for ( var i = 0; i < groupCount; i++ ) {
35434           // make an array of colY values for that one group
35435           var groupColYs = this.colYs.slice( i, i + colSpan );
35436           // and get the max value of the array
35437           colGroup[i] = Math.max.apply( Math, groupColYs );
35438         }
35439         return colGroup;
35440     },
35441     /*
35442     _manageStamp : function( stamp )
35443     {
35444         var stampSize =  stamp.getSize();
35445         var offset = stamp.getBox();
35446         // get the columns that this stamp affects
35447         var firstX = this.isOriginLeft ? offset.x : offset.right;
35448         var lastX = firstX + stampSize.width;
35449         var firstCol = Math.floor( firstX / this.columnWidth );
35450         firstCol = Math.max( 0, firstCol );
35451         
35452         var lastCol = Math.floor( lastX / this.columnWidth );
35453         // lastCol should not go over if multiple of columnWidth #425
35454         lastCol -= lastX % this.columnWidth ? 0 : 1;
35455         lastCol = Math.min( this.cols - 1, lastCol );
35456         
35457         // set colYs to bottom of the stamp
35458         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35459             stampSize.height;
35460             
35461         for ( var i = firstCol; i <= lastCol; i++ ) {
35462           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35463         }
35464     },
35465     */
35466     
35467     _getContainerSize : function()
35468     {
35469         this.maxY = Math.max.apply( Math, this.colYs );
35470         var size = {
35471             height: this.maxY
35472         };
35473       
35474         if ( this.isFitWidth ) {
35475             size.width = this._getContainerFitWidth();
35476         }
35477       
35478         return size;
35479     },
35480     
35481     _getContainerFitWidth : function()
35482     {
35483         var unusedCols = 0;
35484         // count unused columns
35485         var i = this.cols;
35486         while ( --i ) {
35487           if ( this.colYs[i] !== 0 ) {
35488             break;
35489           }
35490           unusedCols++;
35491         }
35492         // fit container to columns that have been used
35493         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35494     },
35495     
35496     needsResizeLayout : function()
35497     {
35498         var previousWidth = this.containerWidth;
35499         this.getContainerWidth();
35500         return previousWidth !== this.containerWidth;
35501     }
35502  
35503 });
35504
35505  
35506
35507  /*
35508  * - LGPL
35509  *
35510  * element
35511  * 
35512  */
35513
35514 /**
35515  * @class Roo.bootstrap.MasonryBrick
35516  * @extends Roo.bootstrap.Component
35517  * Bootstrap MasonryBrick class
35518  * 
35519  * @constructor
35520  * Create a new MasonryBrick
35521  * @param {Object} config The config object
35522  */
35523
35524 Roo.bootstrap.MasonryBrick = function(config){
35525     
35526     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35527     
35528     Roo.bootstrap.MasonryBrick.register(this);
35529     
35530     this.addEvents({
35531         // raw events
35532         /**
35533          * @event click
35534          * When a MasonryBrick is clcik
35535          * @param {Roo.bootstrap.MasonryBrick} this
35536          * @param {Roo.EventObject} e
35537          */
35538         "click" : true
35539     });
35540 };
35541
35542 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35543     
35544     /**
35545      * @cfg {String} title
35546      */   
35547     title : '',
35548     /**
35549      * @cfg {String} html
35550      */   
35551     html : '',
35552     /**
35553      * @cfg {String} bgimage
35554      */   
35555     bgimage : '',
35556     /**
35557      * @cfg {String} videourl
35558      */   
35559     videourl : '',
35560     /**
35561      * @cfg {String} cls
35562      */   
35563     cls : '',
35564     /**
35565      * @cfg {String} href
35566      */   
35567     href : '',
35568     /**
35569      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35570      */   
35571     size : 'xs',
35572     
35573     /**
35574      * @cfg {String} placetitle (center|bottom)
35575      */   
35576     placetitle : '',
35577     
35578     /**
35579      * @cfg {Boolean} isFitContainer defalut true
35580      */   
35581     isFitContainer : true, 
35582     
35583     /**
35584      * @cfg {Boolean} preventDefault defalut false
35585      */   
35586     preventDefault : false, 
35587     
35588     /**
35589      * @cfg {Boolean} inverse defalut false
35590      */   
35591     maskInverse : false, 
35592     
35593     getAutoCreate : function()
35594     {
35595         if(!this.isFitContainer){
35596             return this.getSplitAutoCreate();
35597         }
35598         
35599         var cls = 'masonry-brick masonry-brick-full';
35600         
35601         if(this.href.length){
35602             cls += ' masonry-brick-link';
35603         }
35604         
35605         if(this.bgimage.length){
35606             cls += ' masonry-brick-image';
35607         }
35608         
35609         if(this.maskInverse){
35610             cls += ' mask-inverse';
35611         }
35612         
35613         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35614             cls += ' enable-mask';
35615         }
35616         
35617         if(this.size){
35618             cls += ' masonry-' + this.size + '-brick';
35619         }
35620         
35621         if(this.placetitle.length){
35622             
35623             switch (this.placetitle) {
35624                 case 'center' :
35625                     cls += ' masonry-center-title';
35626                     break;
35627                 case 'bottom' :
35628                     cls += ' masonry-bottom-title';
35629                     break;
35630                 default:
35631                     break;
35632             }
35633             
35634         } else {
35635             if(!this.html.length && !this.bgimage.length){
35636                 cls += ' masonry-center-title';
35637             }
35638
35639             if(!this.html.length && this.bgimage.length){
35640                 cls += ' masonry-bottom-title';
35641             }
35642         }
35643         
35644         if(this.cls){
35645             cls += ' ' + this.cls;
35646         }
35647         
35648         var cfg = {
35649             tag: (this.href.length) ? 'a' : 'div',
35650             cls: cls,
35651             cn: [
35652                 {
35653                     tag: 'div',
35654                     cls: 'masonry-brick-mask'
35655                 },
35656                 {
35657                     tag: 'div',
35658                     cls: 'masonry-brick-paragraph',
35659                     cn: []
35660                 }
35661             ]
35662         };
35663         
35664         if(this.href.length){
35665             cfg.href = this.href;
35666         }
35667         
35668         var cn = cfg.cn[1].cn;
35669         
35670         if(this.title.length){
35671             cn.push({
35672                 tag: 'h4',
35673                 cls: 'masonry-brick-title',
35674                 html: this.title
35675             });
35676         }
35677         
35678         if(this.html.length){
35679             cn.push({
35680                 tag: 'p',
35681                 cls: 'masonry-brick-text',
35682                 html: this.html
35683             });
35684         }
35685         
35686         if (!this.title.length && !this.html.length) {
35687             cfg.cn[1].cls += ' hide';
35688         }
35689         
35690         if(this.bgimage.length){
35691             cfg.cn.push({
35692                 tag: 'img',
35693                 cls: 'masonry-brick-image-view',
35694                 src: this.bgimage
35695             });
35696         }
35697         
35698         if(this.videourl.length){
35699             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35700             // youtube support only?
35701             cfg.cn.push({
35702                 tag: 'iframe',
35703                 cls: 'masonry-brick-image-view',
35704                 src: vurl,
35705                 frameborder : 0,
35706                 allowfullscreen : true
35707             });
35708         }
35709         
35710         return cfg;
35711         
35712     },
35713     
35714     getSplitAutoCreate : function()
35715     {
35716         var cls = 'masonry-brick masonry-brick-split';
35717         
35718         if(this.href.length){
35719             cls += ' masonry-brick-link';
35720         }
35721         
35722         if(this.bgimage.length){
35723             cls += ' masonry-brick-image';
35724         }
35725         
35726         if(this.size){
35727             cls += ' masonry-' + this.size + '-brick';
35728         }
35729         
35730         switch (this.placetitle) {
35731             case 'center' :
35732                 cls += ' masonry-center-title';
35733                 break;
35734             case 'bottom' :
35735                 cls += ' masonry-bottom-title';
35736                 break;
35737             default:
35738                 if(!this.bgimage.length){
35739                     cls += ' masonry-center-title';
35740                 }
35741
35742                 if(this.bgimage.length){
35743                     cls += ' masonry-bottom-title';
35744                 }
35745                 break;
35746         }
35747         
35748         if(this.cls){
35749             cls += ' ' + this.cls;
35750         }
35751         
35752         var cfg = {
35753             tag: (this.href.length) ? 'a' : 'div',
35754             cls: cls,
35755             cn: [
35756                 {
35757                     tag: 'div',
35758                     cls: 'masonry-brick-split-head',
35759                     cn: [
35760                         {
35761                             tag: 'div',
35762                             cls: 'masonry-brick-paragraph',
35763                             cn: []
35764                         }
35765                     ]
35766                 },
35767                 {
35768                     tag: 'div',
35769                     cls: 'masonry-brick-split-body',
35770                     cn: []
35771                 }
35772             ]
35773         };
35774         
35775         if(this.href.length){
35776             cfg.href = this.href;
35777         }
35778         
35779         if(this.title.length){
35780             cfg.cn[0].cn[0].cn.push({
35781                 tag: 'h4',
35782                 cls: 'masonry-brick-title',
35783                 html: this.title
35784             });
35785         }
35786         
35787         if(this.html.length){
35788             cfg.cn[1].cn.push({
35789                 tag: 'p',
35790                 cls: 'masonry-brick-text',
35791                 html: this.html
35792             });
35793         }
35794
35795         if(this.bgimage.length){
35796             cfg.cn[0].cn.push({
35797                 tag: 'img',
35798                 cls: 'masonry-brick-image-view',
35799                 src: this.bgimage
35800             });
35801         }
35802         
35803         if(this.videourl.length){
35804             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35805             // youtube support only?
35806             cfg.cn[0].cn.cn.push({
35807                 tag: 'iframe',
35808                 cls: 'masonry-brick-image-view',
35809                 src: vurl,
35810                 frameborder : 0,
35811                 allowfullscreen : true
35812             });
35813         }
35814         
35815         return cfg;
35816     },
35817     
35818     initEvents: function() 
35819     {
35820         switch (this.size) {
35821             case 'xs' :
35822                 this.x = 1;
35823                 this.y = 1;
35824                 break;
35825             case 'sm' :
35826                 this.x = 2;
35827                 this.y = 2;
35828                 break;
35829             case 'md' :
35830             case 'md-left' :
35831             case 'md-right' :
35832                 this.x = 3;
35833                 this.y = 3;
35834                 break;
35835             case 'tall' :
35836                 this.x = 2;
35837                 this.y = 3;
35838                 break;
35839             case 'wide' :
35840                 this.x = 3;
35841                 this.y = 2;
35842                 break;
35843             case 'wide-thin' :
35844                 this.x = 3;
35845                 this.y = 1;
35846                 break;
35847                         
35848             default :
35849                 break;
35850         }
35851         
35852         if(Roo.isTouch){
35853             this.el.on('touchstart', this.onTouchStart, this);
35854             this.el.on('touchmove', this.onTouchMove, this);
35855             this.el.on('touchend', this.onTouchEnd, this);
35856             this.el.on('contextmenu', this.onContextMenu, this);
35857         } else {
35858             this.el.on('mouseenter'  ,this.enter, this);
35859             this.el.on('mouseleave', this.leave, this);
35860             this.el.on('click', this.onClick, this);
35861         }
35862         
35863         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35864             this.parent().bricks.push(this);   
35865         }
35866         
35867     },
35868     
35869     onClick: function(e, el)
35870     {
35871         var time = this.endTimer - this.startTimer;
35872         // Roo.log(e.preventDefault());
35873         if(Roo.isTouch){
35874             if(time > 1000){
35875                 e.preventDefault();
35876                 return;
35877             }
35878         }
35879         
35880         if(!this.preventDefault){
35881             return;
35882         }
35883         
35884         e.preventDefault();
35885         
35886         if (this.activeClass != '') {
35887             this.selectBrick();
35888         }
35889         
35890         this.fireEvent('click', this, e);
35891     },
35892     
35893     enter: function(e, el)
35894     {
35895         e.preventDefault();
35896         
35897         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35898             return;
35899         }
35900         
35901         if(this.bgimage.length && this.html.length){
35902             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35903         }
35904     },
35905     
35906     leave: function(e, el)
35907     {
35908         e.preventDefault();
35909         
35910         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35911             return;
35912         }
35913         
35914         if(this.bgimage.length && this.html.length){
35915             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35916         }
35917     },
35918     
35919     onTouchStart: function(e, el)
35920     {
35921 //        e.preventDefault();
35922         
35923         this.touchmoved = false;
35924         
35925         if(!this.isFitContainer){
35926             return;
35927         }
35928         
35929         if(!this.bgimage.length || !this.html.length){
35930             return;
35931         }
35932         
35933         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35934         
35935         this.timer = new Date().getTime();
35936         
35937     },
35938     
35939     onTouchMove: function(e, el)
35940     {
35941         this.touchmoved = true;
35942     },
35943     
35944     onContextMenu : function(e,el)
35945     {
35946         e.preventDefault();
35947         e.stopPropagation();
35948         return false;
35949     },
35950     
35951     onTouchEnd: function(e, el)
35952     {
35953 //        e.preventDefault();
35954         
35955         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35956         
35957             this.leave(e,el);
35958             
35959             return;
35960         }
35961         
35962         if(!this.bgimage.length || !this.html.length){
35963             
35964             if(this.href.length){
35965                 window.location.href = this.href;
35966             }
35967             
35968             return;
35969         }
35970         
35971         if(!this.isFitContainer){
35972             return;
35973         }
35974         
35975         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35976         
35977         window.location.href = this.href;
35978     },
35979     
35980     //selection on single brick only
35981     selectBrick : function() {
35982         
35983         if (!this.parentId) {
35984             return;
35985         }
35986         
35987         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35988         var index = m.selectedBrick.indexOf(this.id);
35989         
35990         if ( index > -1) {
35991             m.selectedBrick.splice(index,1);
35992             this.el.removeClass(this.activeClass);
35993             return;
35994         }
35995         
35996         for(var i = 0; i < m.selectedBrick.length; i++) {
35997             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35998             b.el.removeClass(b.activeClass);
35999         }
36000         
36001         m.selectedBrick = [];
36002         
36003         m.selectedBrick.push(this.id);
36004         this.el.addClass(this.activeClass);
36005         return;
36006     },
36007     
36008     isSelected : function(){
36009         return this.el.hasClass(this.activeClass);
36010         
36011     }
36012 });
36013
36014 Roo.apply(Roo.bootstrap.MasonryBrick, {
36015     
36016     //groups: {},
36017     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36018      /**
36019     * register a Masonry Brick
36020     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36021     */
36022     
36023     register : function(brick)
36024     {
36025         //this.groups[brick.id] = brick;
36026         this.groups.add(brick.id, brick);
36027     },
36028     /**
36029     * fetch a  masonry brick based on the masonry brick ID
36030     * @param {string} the masonry brick to add
36031     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36032     */
36033     
36034     get: function(brick_id) 
36035     {
36036         // if (typeof(this.groups[brick_id]) == 'undefined') {
36037         //     return false;
36038         // }
36039         // return this.groups[brick_id] ;
36040         
36041         if(this.groups.key(brick_id)) {
36042             return this.groups.key(brick_id);
36043         }
36044         
36045         return false;
36046     }
36047     
36048     
36049     
36050 });
36051
36052  /*
36053  * - LGPL
36054  *
36055  * element
36056  * 
36057  */
36058
36059 /**
36060  * @class Roo.bootstrap.Brick
36061  * @extends Roo.bootstrap.Component
36062  * Bootstrap Brick class
36063  * 
36064  * @constructor
36065  * Create a new Brick
36066  * @param {Object} config The config object
36067  */
36068
36069 Roo.bootstrap.Brick = function(config){
36070     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36071     
36072     this.addEvents({
36073         // raw events
36074         /**
36075          * @event click
36076          * When a Brick is click
36077          * @param {Roo.bootstrap.Brick} this
36078          * @param {Roo.EventObject} e
36079          */
36080         "click" : true
36081     });
36082 };
36083
36084 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36085     
36086     /**
36087      * @cfg {String} title
36088      */   
36089     title : '',
36090     /**
36091      * @cfg {String} html
36092      */   
36093     html : '',
36094     /**
36095      * @cfg {String} bgimage
36096      */   
36097     bgimage : '',
36098     /**
36099      * @cfg {String} cls
36100      */   
36101     cls : '',
36102     /**
36103      * @cfg {String} href
36104      */   
36105     href : '',
36106     /**
36107      * @cfg {String} video
36108      */   
36109     video : '',
36110     /**
36111      * @cfg {Boolean} square
36112      */   
36113     square : true,
36114     
36115     getAutoCreate : function()
36116     {
36117         var cls = 'roo-brick';
36118         
36119         if(this.href.length){
36120             cls += ' roo-brick-link';
36121         }
36122         
36123         if(this.bgimage.length){
36124             cls += ' roo-brick-image';
36125         }
36126         
36127         if(!this.html.length && !this.bgimage.length){
36128             cls += ' roo-brick-center-title';
36129         }
36130         
36131         if(!this.html.length && this.bgimage.length){
36132             cls += ' roo-brick-bottom-title';
36133         }
36134         
36135         if(this.cls){
36136             cls += ' ' + this.cls;
36137         }
36138         
36139         var cfg = {
36140             tag: (this.href.length) ? 'a' : 'div',
36141             cls: cls,
36142             cn: [
36143                 {
36144                     tag: 'div',
36145                     cls: 'roo-brick-paragraph',
36146                     cn: []
36147                 }
36148             ]
36149         };
36150         
36151         if(this.href.length){
36152             cfg.href = this.href;
36153         }
36154         
36155         var cn = cfg.cn[0].cn;
36156         
36157         if(this.title.length){
36158             cn.push({
36159                 tag: 'h4',
36160                 cls: 'roo-brick-title',
36161                 html: this.title
36162             });
36163         }
36164         
36165         if(this.html.length){
36166             cn.push({
36167                 tag: 'p',
36168                 cls: 'roo-brick-text',
36169                 html: this.html
36170             });
36171         } else {
36172             cn.cls += ' hide';
36173         }
36174         
36175         if(this.bgimage.length){
36176             cfg.cn.push({
36177                 tag: 'img',
36178                 cls: 'roo-brick-image-view',
36179                 src: this.bgimage
36180             });
36181         }
36182         
36183         return cfg;
36184     },
36185     
36186     initEvents: function() 
36187     {
36188         if(this.title.length || this.html.length){
36189             this.el.on('mouseenter'  ,this.enter, this);
36190             this.el.on('mouseleave', this.leave, this);
36191         }
36192         
36193         Roo.EventManager.onWindowResize(this.resize, this); 
36194         
36195         if(this.bgimage.length){
36196             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36197             this.imageEl.on('load', this.onImageLoad, this);
36198             return;
36199         }
36200         
36201         this.resize();
36202     },
36203     
36204     onImageLoad : function()
36205     {
36206         this.resize();
36207     },
36208     
36209     resize : function()
36210     {
36211         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36212         
36213         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36214         
36215         if(this.bgimage.length){
36216             var image = this.el.select('.roo-brick-image-view', true).first();
36217             
36218             image.setWidth(paragraph.getWidth());
36219             
36220             if(this.square){
36221                 image.setHeight(paragraph.getWidth());
36222             }
36223             
36224             this.el.setHeight(image.getHeight());
36225             paragraph.setHeight(image.getHeight());
36226             
36227         }
36228         
36229     },
36230     
36231     enter: function(e, el)
36232     {
36233         e.preventDefault();
36234         
36235         if(this.bgimage.length){
36236             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36237             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36238         }
36239     },
36240     
36241     leave: function(e, el)
36242     {
36243         e.preventDefault();
36244         
36245         if(this.bgimage.length){
36246             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36247             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36248         }
36249     }
36250     
36251 });
36252
36253  
36254
36255  /*
36256  * - LGPL
36257  *
36258  * Number field 
36259  */
36260
36261 /**
36262  * @class Roo.bootstrap.NumberField
36263  * @extends Roo.bootstrap.Input
36264  * Bootstrap NumberField class
36265  * 
36266  * 
36267  * 
36268  * 
36269  * @constructor
36270  * Create a new NumberField
36271  * @param {Object} config The config object
36272  */
36273
36274 Roo.bootstrap.NumberField = function(config){
36275     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36276 };
36277
36278 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36279     
36280     /**
36281      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36282      */
36283     allowDecimals : true,
36284     /**
36285      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36286      */
36287     decimalSeparator : ".",
36288     /**
36289      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36290      */
36291     decimalPrecision : 2,
36292     /**
36293      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36294      */
36295     allowNegative : true,
36296     
36297     /**
36298      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36299      */
36300     allowZero: true,
36301     /**
36302      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36303      */
36304     minValue : Number.NEGATIVE_INFINITY,
36305     /**
36306      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36307      */
36308     maxValue : Number.MAX_VALUE,
36309     /**
36310      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36311      */
36312     minText : "The minimum value for this field is {0}",
36313     /**
36314      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36315      */
36316     maxText : "The maximum value for this field is {0}",
36317     /**
36318      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36319      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36320      */
36321     nanText : "{0} is not a valid number",
36322     /**
36323      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36324      */
36325     thousandsDelimiter : false,
36326     /**
36327      * @cfg {String} valueAlign alignment of value
36328      */
36329     valueAlign : "left",
36330
36331     getAutoCreate : function()
36332     {
36333         var hiddenInput = {
36334             tag: 'input',
36335             type: 'hidden',
36336             id: Roo.id(),
36337             cls: 'hidden-number-input'
36338         };
36339         
36340         if (this.name) {
36341             hiddenInput.name = this.name;
36342         }
36343         
36344         this.name = '';
36345         
36346         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36347         
36348         this.name = hiddenInput.name;
36349         
36350         if(cfg.cn.length > 0) {
36351             cfg.cn.push(hiddenInput);
36352         }
36353         
36354         return cfg;
36355     },
36356
36357     // private
36358     initEvents : function()
36359     {   
36360         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36361         
36362         var allowed = "0123456789";
36363         
36364         if(this.allowDecimals){
36365             allowed += this.decimalSeparator;
36366         }
36367         
36368         if(this.allowNegative){
36369             allowed += "-";
36370         }
36371         
36372         if(this.thousandsDelimiter) {
36373             allowed += ",";
36374         }
36375         
36376         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36377         
36378         var keyPress = function(e){
36379             
36380             var k = e.getKey();
36381             
36382             var c = e.getCharCode();
36383             
36384             if(
36385                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36386                     allowed.indexOf(String.fromCharCode(c)) === -1
36387             ){
36388                 e.stopEvent();
36389                 return;
36390             }
36391             
36392             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36393                 return;
36394             }
36395             
36396             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36397                 e.stopEvent();
36398             }
36399         };
36400         
36401         this.el.on("keypress", keyPress, this);
36402     },
36403     
36404     validateValue : function(value)
36405     {
36406         
36407         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36408             return false;
36409         }
36410         
36411         var num = this.parseValue(value);
36412         
36413         if(isNaN(num)){
36414             this.markInvalid(String.format(this.nanText, value));
36415             return false;
36416         }
36417         
36418         if(num < this.minValue){
36419             this.markInvalid(String.format(this.minText, this.minValue));
36420             return false;
36421         }
36422         
36423         if(num > this.maxValue){
36424             this.markInvalid(String.format(this.maxText, this.maxValue));
36425             return false;
36426         }
36427         
36428         return true;
36429     },
36430
36431     getValue : function()
36432     {
36433         var v = this.hiddenEl().getValue();
36434         
36435         return this.fixPrecision(this.parseValue(v));
36436     },
36437
36438     parseValue : function(value)
36439     {
36440         if(this.thousandsDelimiter) {
36441             value += "";
36442             r = new RegExp(",", "g");
36443             value = value.replace(r, "");
36444         }
36445         
36446         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36447         return isNaN(value) ? '' : value;
36448     },
36449
36450     fixPrecision : function(value)
36451     {
36452         if(this.thousandsDelimiter) {
36453             value += "";
36454             r = new RegExp(",", "g");
36455             value = value.replace(r, "");
36456         }
36457         
36458         var nan = isNaN(value);
36459         
36460         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36461             return nan ? '' : value;
36462         }
36463         return parseFloat(value).toFixed(this.decimalPrecision);
36464     },
36465
36466     setValue : function(v)
36467     {
36468         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36469         
36470         this.value = v;
36471         
36472         if(this.rendered){
36473             
36474             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36475             
36476             this.inputEl().dom.value = (v == '') ? '' :
36477                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36478             
36479             if(!this.allowZero && v === '0') {
36480                 this.hiddenEl().dom.value = '';
36481                 this.inputEl().dom.value = '';
36482             }
36483             
36484             this.validate();
36485         }
36486     },
36487
36488     decimalPrecisionFcn : function(v)
36489     {
36490         return Math.floor(v);
36491     },
36492
36493     beforeBlur : function()
36494     {
36495         var v = this.parseValue(this.getRawValue());
36496         
36497         if(v || v === 0 || v === ''){
36498             this.setValue(v);
36499         }
36500     },
36501     
36502     hiddenEl : function()
36503     {
36504         return this.el.select('input.hidden-number-input',true).first();
36505     }
36506     
36507 });
36508
36509  
36510
36511 /*
36512 * Licence: LGPL
36513 */
36514
36515 /**
36516  * @class Roo.bootstrap.DocumentSlider
36517  * @extends Roo.bootstrap.Component
36518  * Bootstrap DocumentSlider class
36519  * 
36520  * @constructor
36521  * Create a new DocumentViewer
36522  * @param {Object} config The config object
36523  */
36524
36525 Roo.bootstrap.DocumentSlider = function(config){
36526     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36527     
36528     this.files = [];
36529     
36530     this.addEvents({
36531         /**
36532          * @event initial
36533          * Fire after initEvent
36534          * @param {Roo.bootstrap.DocumentSlider} this
36535          */
36536         "initial" : true,
36537         /**
36538          * @event update
36539          * Fire after update
36540          * @param {Roo.bootstrap.DocumentSlider} this
36541          */
36542         "update" : true,
36543         /**
36544          * @event click
36545          * Fire after click
36546          * @param {Roo.bootstrap.DocumentSlider} this
36547          */
36548         "click" : true
36549     });
36550 };
36551
36552 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36553     
36554     files : false,
36555     
36556     indicator : 0,
36557     
36558     getAutoCreate : function()
36559     {
36560         var cfg = {
36561             tag : 'div',
36562             cls : 'roo-document-slider',
36563             cn : [
36564                 {
36565                     tag : 'div',
36566                     cls : 'roo-document-slider-header',
36567                     cn : [
36568                         {
36569                             tag : 'div',
36570                             cls : 'roo-document-slider-header-title'
36571                         }
36572                     ]
36573                 },
36574                 {
36575                     tag : 'div',
36576                     cls : 'roo-document-slider-body',
36577                     cn : [
36578                         {
36579                             tag : 'div',
36580                             cls : 'roo-document-slider-prev',
36581                             cn : [
36582                                 {
36583                                     tag : 'i',
36584                                     cls : 'fa fa-chevron-left'
36585                                 }
36586                             ]
36587                         },
36588                         {
36589                             tag : 'div',
36590                             cls : 'roo-document-slider-thumb',
36591                             cn : [
36592                                 {
36593                                     tag : 'img',
36594                                     cls : 'roo-document-slider-image'
36595                                 }
36596                             ]
36597                         },
36598                         {
36599                             tag : 'div',
36600                             cls : 'roo-document-slider-next',
36601                             cn : [
36602                                 {
36603                                     tag : 'i',
36604                                     cls : 'fa fa-chevron-right'
36605                                 }
36606                             ]
36607                         }
36608                     ]
36609                 }
36610             ]
36611         };
36612         
36613         return cfg;
36614     },
36615     
36616     initEvents : function()
36617     {
36618         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36619         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36620         
36621         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36622         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36623         
36624         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36625         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36626         
36627         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36628         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36629         
36630         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36631         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36632         
36633         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36634         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36635         
36636         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36637         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36638         
36639         this.thumbEl.on('click', this.onClick, this);
36640         
36641         this.prevIndicator.on('click', this.prev, this);
36642         
36643         this.nextIndicator.on('click', this.next, this);
36644         
36645     },
36646     
36647     initial : function()
36648     {
36649         if(this.files.length){
36650             this.indicator = 1;
36651             this.update()
36652         }
36653         
36654         this.fireEvent('initial', this);
36655     },
36656     
36657     update : function()
36658     {
36659         this.imageEl.attr('src', this.files[this.indicator - 1]);
36660         
36661         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36662         
36663         this.prevIndicator.show();
36664         
36665         if(this.indicator == 1){
36666             this.prevIndicator.hide();
36667         }
36668         
36669         this.nextIndicator.show();
36670         
36671         if(this.indicator == this.files.length){
36672             this.nextIndicator.hide();
36673         }
36674         
36675         this.thumbEl.scrollTo('top');
36676         
36677         this.fireEvent('update', this);
36678     },
36679     
36680     onClick : function(e)
36681     {
36682         e.preventDefault();
36683         
36684         this.fireEvent('click', this);
36685     },
36686     
36687     prev : function(e)
36688     {
36689         e.preventDefault();
36690         
36691         this.indicator = Math.max(1, this.indicator - 1);
36692         
36693         this.update();
36694     },
36695     
36696     next : function(e)
36697     {
36698         e.preventDefault();
36699         
36700         this.indicator = Math.min(this.files.length, this.indicator + 1);
36701         
36702         this.update();
36703     }
36704 });
36705 /*
36706  * - LGPL
36707  *
36708  * RadioSet
36709  *
36710  *
36711  */
36712
36713 /**
36714  * @class Roo.bootstrap.RadioSet
36715  * @extends Roo.bootstrap.Input
36716  * Bootstrap RadioSet class
36717  * @cfg {String} indicatorpos (left|right) default left
36718  * @cfg {Boolean} inline (true|false) inline the element (default true)
36719  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36720  * @constructor
36721  * Create a new RadioSet
36722  * @param {Object} config The config object
36723  */
36724
36725 Roo.bootstrap.RadioSet = function(config){
36726     
36727     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36728     
36729     this.radioes = [];
36730     
36731     Roo.bootstrap.RadioSet.register(this);
36732     
36733     this.addEvents({
36734         /**
36735         * @event check
36736         * Fires when the element is checked or unchecked.
36737         * @param {Roo.bootstrap.RadioSet} this This radio
36738         * @param {Roo.bootstrap.Radio} item The checked item
36739         */
36740        check : true,
36741        /**
36742         * @event click
36743         * Fires when the element is click.
36744         * @param {Roo.bootstrap.RadioSet} this This radio set
36745         * @param {Roo.bootstrap.Radio} item The checked item
36746         * @param {Roo.EventObject} e The event object
36747         */
36748        click : true
36749     });
36750     
36751 };
36752
36753 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36754
36755     radioes : false,
36756     
36757     inline : true,
36758     
36759     weight : '',
36760     
36761     indicatorpos : 'left',
36762     
36763     getAutoCreate : function()
36764     {
36765         var label = {
36766             tag : 'label',
36767             cls : 'roo-radio-set-label',
36768             cn : [
36769                 {
36770                     tag : 'span',
36771                     html : this.fieldLabel
36772                 }
36773             ]
36774         };
36775         if (Roo.bootstrap.version == 3) {
36776             
36777             
36778             if(this.indicatorpos == 'left'){
36779                 label.cn.unshift({
36780                     tag : 'i',
36781                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36782                     tooltip : 'This field is required'
36783                 });
36784             } else {
36785                 label.cn.push({
36786                     tag : 'i',
36787                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36788                     tooltip : 'This field is required'
36789                 });
36790             }
36791         }
36792         var items = {
36793             tag : 'div',
36794             cls : 'roo-radio-set-items'
36795         };
36796         
36797         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36798         
36799         if (align === 'left' && this.fieldLabel.length) {
36800             
36801             items = {
36802                 cls : "roo-radio-set-right", 
36803                 cn: [
36804                     items
36805                 ]
36806             };
36807             
36808             if(this.labelWidth > 12){
36809                 label.style = "width: " + this.labelWidth + 'px';
36810             }
36811             
36812             if(this.labelWidth < 13 && this.labelmd == 0){
36813                 this.labelmd = this.labelWidth;
36814             }
36815             
36816             if(this.labellg > 0){
36817                 label.cls += ' col-lg-' + this.labellg;
36818                 items.cls += ' col-lg-' + (12 - this.labellg);
36819             }
36820             
36821             if(this.labelmd > 0){
36822                 label.cls += ' col-md-' + this.labelmd;
36823                 items.cls += ' col-md-' + (12 - this.labelmd);
36824             }
36825             
36826             if(this.labelsm > 0){
36827                 label.cls += ' col-sm-' + this.labelsm;
36828                 items.cls += ' col-sm-' + (12 - this.labelsm);
36829             }
36830             
36831             if(this.labelxs > 0){
36832                 label.cls += ' col-xs-' + this.labelxs;
36833                 items.cls += ' col-xs-' + (12 - this.labelxs);
36834             }
36835         }
36836         
36837         var cfg = {
36838             tag : 'div',
36839             cls : 'roo-radio-set',
36840             cn : [
36841                 {
36842                     tag : 'input',
36843                     cls : 'roo-radio-set-input',
36844                     type : 'hidden',
36845                     name : this.name,
36846                     value : this.value ? this.value :  ''
36847                 },
36848                 label,
36849                 items
36850             ]
36851         };
36852         
36853         if(this.weight.length){
36854             cfg.cls += ' roo-radio-' + this.weight;
36855         }
36856         
36857         if(this.inline) {
36858             cfg.cls += ' roo-radio-set-inline';
36859         }
36860         
36861         var settings=this;
36862         ['xs','sm','md','lg'].map(function(size){
36863             if (settings[size]) {
36864                 cfg.cls += ' col-' + size + '-' + settings[size];
36865             }
36866         });
36867         
36868         return cfg;
36869         
36870     },
36871
36872     initEvents : function()
36873     {
36874         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36875         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36876         
36877         if(!this.fieldLabel.length){
36878             this.labelEl.hide();
36879         }
36880         
36881         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36882         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36883         
36884         this.indicator = this.indicatorEl();
36885         
36886         if(this.indicator){
36887             this.indicator.addClass('invisible');
36888         }
36889         
36890         this.originalValue = this.getValue();
36891         
36892     },
36893     
36894     inputEl: function ()
36895     {
36896         return this.el.select('.roo-radio-set-input', true).first();
36897     },
36898     
36899     getChildContainer : function()
36900     {
36901         return this.itemsEl;
36902     },
36903     
36904     register : function(item)
36905     {
36906         this.radioes.push(item);
36907         
36908     },
36909     
36910     validate : function()
36911     {   
36912         if(this.getVisibilityEl().hasClass('hidden')){
36913             return true;
36914         }
36915         
36916         var valid = false;
36917         
36918         Roo.each(this.radioes, function(i){
36919             if(!i.checked){
36920                 return;
36921             }
36922             
36923             valid = true;
36924             return false;
36925         });
36926         
36927         if(this.allowBlank) {
36928             return true;
36929         }
36930         
36931         if(this.disabled || valid){
36932             this.markValid();
36933             return true;
36934         }
36935         
36936         this.markInvalid();
36937         return false;
36938         
36939     },
36940     
36941     markValid : function()
36942     {
36943         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36944             this.indicatorEl().removeClass('visible');
36945             this.indicatorEl().addClass('invisible');
36946         }
36947         
36948         
36949         if (Roo.bootstrap.version == 3) {
36950             this.el.removeClass([this.invalidClass, this.validClass]);
36951             this.el.addClass(this.validClass);
36952         } else {
36953             this.el.removeClass(['is-invalid','is-valid']);
36954             this.el.addClass(['is-valid']);
36955         }
36956         this.fireEvent('valid', this);
36957     },
36958     
36959     markInvalid : function(msg)
36960     {
36961         if(this.allowBlank || this.disabled){
36962             return;
36963         }
36964         
36965         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36966             this.indicatorEl().removeClass('invisible');
36967             this.indicatorEl().addClass('visible');
36968         }
36969         if (Roo.bootstrap.version == 3) {
36970             this.el.removeClass([this.invalidClass, this.validClass]);
36971             this.el.addClass(this.invalidClass);
36972         } else {
36973             this.el.removeClass(['is-invalid','is-valid']);
36974             this.el.addClass(['is-invalid']);
36975         }
36976         
36977         this.fireEvent('invalid', this, msg);
36978         
36979     },
36980     
36981     setValue : function(v, suppressEvent)
36982     {   
36983         if(this.value === v){
36984             return;
36985         }
36986         
36987         this.value = v;
36988         
36989         if(this.rendered){
36990             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36991         }
36992         
36993         Roo.each(this.radioes, function(i){
36994             i.checked = false;
36995             i.el.removeClass('checked');
36996         });
36997         
36998         Roo.each(this.radioes, function(i){
36999             
37000             if(i.value === v || i.value.toString() === v.toString()){
37001                 i.checked = true;
37002                 i.el.addClass('checked');
37003                 
37004                 if(suppressEvent !== true){
37005                     this.fireEvent('check', this, i);
37006                 }
37007                 
37008                 return false;
37009             }
37010             
37011         }, this);
37012         
37013         this.validate();
37014     },
37015     
37016     clearInvalid : function(){
37017         
37018         if(!this.el || this.preventMark){
37019             return;
37020         }
37021         
37022         this.el.removeClass([this.invalidClass]);
37023         
37024         this.fireEvent('valid', this);
37025     }
37026     
37027 });
37028
37029 Roo.apply(Roo.bootstrap.RadioSet, {
37030     
37031     groups: {},
37032     
37033     register : function(set)
37034     {
37035         this.groups[set.name] = set;
37036     },
37037     
37038     get: function(name) 
37039     {
37040         if (typeof(this.groups[name]) == 'undefined') {
37041             return false;
37042         }
37043         
37044         return this.groups[name] ;
37045     }
37046     
37047 });
37048 /*
37049  * Based on:
37050  * Ext JS Library 1.1.1
37051  * Copyright(c) 2006-2007, Ext JS, LLC.
37052  *
37053  * Originally Released Under LGPL - original licence link has changed is not relivant.
37054  *
37055  * Fork - LGPL
37056  * <script type="text/javascript">
37057  */
37058
37059
37060 /**
37061  * @class Roo.bootstrap.SplitBar
37062  * @extends Roo.util.Observable
37063  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37064  * <br><br>
37065  * Usage:
37066  * <pre><code>
37067 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37068                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37069 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37070 split.minSize = 100;
37071 split.maxSize = 600;
37072 split.animate = true;
37073 split.on('moved', splitterMoved);
37074 </code></pre>
37075  * @constructor
37076  * Create a new SplitBar
37077  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37078  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37079  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37080  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37081                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37082                         position of the SplitBar).
37083  */
37084 Roo.bootstrap.SplitBar = function(cfg){
37085     
37086     /** @private */
37087     
37088     //{
37089     //  dragElement : elm
37090     //  resizingElement: el,
37091         // optional..
37092     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37093     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37094         // existingProxy ???
37095     //}
37096     
37097     this.el = Roo.get(cfg.dragElement, true);
37098     this.el.dom.unselectable = "on";
37099     /** @private */
37100     this.resizingEl = Roo.get(cfg.resizingElement, true);
37101
37102     /**
37103      * @private
37104      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37105      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37106      * @type Number
37107      */
37108     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37109     
37110     /**
37111      * The minimum size of the resizing element. (Defaults to 0)
37112      * @type Number
37113      */
37114     this.minSize = 0;
37115     
37116     /**
37117      * The maximum size of the resizing element. (Defaults to 2000)
37118      * @type Number
37119      */
37120     this.maxSize = 2000;
37121     
37122     /**
37123      * Whether to animate the transition to the new size
37124      * @type Boolean
37125      */
37126     this.animate = false;
37127     
37128     /**
37129      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37130      * @type Boolean
37131      */
37132     this.useShim = false;
37133     
37134     /** @private */
37135     this.shim = null;
37136     
37137     if(!cfg.existingProxy){
37138         /** @private */
37139         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37140     }else{
37141         this.proxy = Roo.get(cfg.existingProxy).dom;
37142     }
37143     /** @private */
37144     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37145     
37146     /** @private */
37147     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37148     
37149     /** @private */
37150     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37151     
37152     /** @private */
37153     this.dragSpecs = {};
37154     
37155     /**
37156      * @private The adapter to use to positon and resize elements
37157      */
37158     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37159     this.adapter.init(this);
37160     
37161     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37162         /** @private */
37163         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37164         this.el.addClass("roo-splitbar-h");
37165     }else{
37166         /** @private */
37167         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37168         this.el.addClass("roo-splitbar-v");
37169     }
37170     
37171     this.addEvents({
37172         /**
37173          * @event resize
37174          * Fires when the splitter is moved (alias for {@link #event-moved})
37175          * @param {Roo.bootstrap.SplitBar} this
37176          * @param {Number} newSize the new width or height
37177          */
37178         "resize" : true,
37179         /**
37180          * @event moved
37181          * Fires when the splitter is moved
37182          * @param {Roo.bootstrap.SplitBar} this
37183          * @param {Number} newSize the new width or height
37184          */
37185         "moved" : true,
37186         /**
37187          * @event beforeresize
37188          * Fires before the splitter is dragged
37189          * @param {Roo.bootstrap.SplitBar} this
37190          */
37191         "beforeresize" : true,
37192
37193         "beforeapply" : true
37194     });
37195
37196     Roo.util.Observable.call(this);
37197 };
37198
37199 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37200     onStartProxyDrag : function(x, y){
37201         this.fireEvent("beforeresize", this);
37202         if(!this.overlay){
37203             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37204             o.unselectable();
37205             o.enableDisplayMode("block");
37206             // all splitbars share the same overlay
37207             Roo.bootstrap.SplitBar.prototype.overlay = o;
37208         }
37209         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37210         this.overlay.show();
37211         Roo.get(this.proxy).setDisplayed("block");
37212         var size = this.adapter.getElementSize(this);
37213         this.activeMinSize = this.getMinimumSize();;
37214         this.activeMaxSize = this.getMaximumSize();;
37215         var c1 = size - this.activeMinSize;
37216         var c2 = Math.max(this.activeMaxSize - size, 0);
37217         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37218             this.dd.resetConstraints();
37219             this.dd.setXConstraint(
37220                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37221                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37222             );
37223             this.dd.setYConstraint(0, 0);
37224         }else{
37225             this.dd.resetConstraints();
37226             this.dd.setXConstraint(0, 0);
37227             this.dd.setYConstraint(
37228                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37229                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37230             );
37231          }
37232         this.dragSpecs.startSize = size;
37233         this.dragSpecs.startPoint = [x, y];
37234         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37235     },
37236     
37237     /** 
37238      * @private Called after the drag operation by the DDProxy
37239      */
37240     onEndProxyDrag : function(e){
37241         Roo.get(this.proxy).setDisplayed(false);
37242         var endPoint = Roo.lib.Event.getXY(e);
37243         if(this.overlay){
37244             this.overlay.hide();
37245         }
37246         var newSize;
37247         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37248             newSize = this.dragSpecs.startSize + 
37249                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37250                     endPoint[0] - this.dragSpecs.startPoint[0] :
37251                     this.dragSpecs.startPoint[0] - endPoint[0]
37252                 );
37253         }else{
37254             newSize = this.dragSpecs.startSize + 
37255                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37256                     endPoint[1] - this.dragSpecs.startPoint[1] :
37257                     this.dragSpecs.startPoint[1] - endPoint[1]
37258                 );
37259         }
37260         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37261         if(newSize != this.dragSpecs.startSize){
37262             if(this.fireEvent('beforeapply', this, newSize) !== false){
37263                 this.adapter.setElementSize(this, newSize);
37264                 this.fireEvent("moved", this, newSize);
37265                 this.fireEvent("resize", this, newSize);
37266             }
37267         }
37268     },
37269     
37270     /**
37271      * Get the adapter this SplitBar uses
37272      * @return The adapter object
37273      */
37274     getAdapter : function(){
37275         return this.adapter;
37276     },
37277     
37278     /**
37279      * Set the adapter this SplitBar uses
37280      * @param {Object} adapter A SplitBar adapter object
37281      */
37282     setAdapter : function(adapter){
37283         this.adapter = adapter;
37284         this.adapter.init(this);
37285     },
37286     
37287     /**
37288      * Gets the minimum size for the resizing element
37289      * @return {Number} The minimum size
37290      */
37291     getMinimumSize : function(){
37292         return this.minSize;
37293     },
37294     
37295     /**
37296      * Sets the minimum size for the resizing element
37297      * @param {Number} minSize The minimum size
37298      */
37299     setMinimumSize : function(minSize){
37300         this.minSize = minSize;
37301     },
37302     
37303     /**
37304      * Gets the maximum size for the resizing element
37305      * @return {Number} The maximum size
37306      */
37307     getMaximumSize : function(){
37308         return this.maxSize;
37309     },
37310     
37311     /**
37312      * Sets the maximum size for the resizing element
37313      * @param {Number} maxSize The maximum size
37314      */
37315     setMaximumSize : function(maxSize){
37316         this.maxSize = maxSize;
37317     },
37318     
37319     /**
37320      * Sets the initialize size for the resizing element
37321      * @param {Number} size The initial size
37322      */
37323     setCurrentSize : function(size){
37324         var oldAnimate = this.animate;
37325         this.animate = false;
37326         this.adapter.setElementSize(this, size);
37327         this.animate = oldAnimate;
37328     },
37329     
37330     /**
37331      * Destroy this splitbar. 
37332      * @param {Boolean} removeEl True to remove the element
37333      */
37334     destroy : function(removeEl){
37335         if(this.shim){
37336             this.shim.remove();
37337         }
37338         this.dd.unreg();
37339         this.proxy.parentNode.removeChild(this.proxy);
37340         if(removeEl){
37341             this.el.remove();
37342         }
37343     }
37344 });
37345
37346 /**
37347  * @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.
37348  */
37349 Roo.bootstrap.SplitBar.createProxy = function(dir){
37350     var proxy = new Roo.Element(document.createElement("div"));
37351     proxy.unselectable();
37352     var cls = 'roo-splitbar-proxy';
37353     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37354     document.body.appendChild(proxy.dom);
37355     return proxy.dom;
37356 };
37357
37358 /** 
37359  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37360  * Default Adapter. It assumes the splitter and resizing element are not positioned
37361  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37362  */
37363 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37364 };
37365
37366 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37367     // do nothing for now
37368     init : function(s){
37369     
37370     },
37371     /**
37372      * Called before drag operations to get the current size of the resizing element. 
37373      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37374      */
37375      getElementSize : function(s){
37376         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37377             return s.resizingEl.getWidth();
37378         }else{
37379             return s.resizingEl.getHeight();
37380         }
37381     },
37382     
37383     /**
37384      * Called after drag operations to set the size of the resizing element.
37385      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37386      * @param {Number} newSize The new size to set
37387      * @param {Function} onComplete A function to be invoked when resizing is complete
37388      */
37389     setElementSize : function(s, newSize, onComplete){
37390         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37391             if(!s.animate){
37392                 s.resizingEl.setWidth(newSize);
37393                 if(onComplete){
37394                     onComplete(s, newSize);
37395                 }
37396             }else{
37397                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37398             }
37399         }else{
37400             
37401             if(!s.animate){
37402                 s.resizingEl.setHeight(newSize);
37403                 if(onComplete){
37404                     onComplete(s, newSize);
37405                 }
37406             }else{
37407                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37408             }
37409         }
37410     }
37411 };
37412
37413 /** 
37414  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37415  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37416  * Adapter that  moves the splitter element to align with the resized sizing element. 
37417  * Used with an absolute positioned SplitBar.
37418  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37419  * document.body, make sure you assign an id to the body element.
37420  */
37421 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37422     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37423     this.container = Roo.get(container);
37424 };
37425
37426 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37427     init : function(s){
37428         this.basic.init(s);
37429     },
37430     
37431     getElementSize : function(s){
37432         return this.basic.getElementSize(s);
37433     },
37434     
37435     setElementSize : function(s, newSize, onComplete){
37436         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37437     },
37438     
37439     moveSplitter : function(s){
37440         var yes = Roo.bootstrap.SplitBar;
37441         switch(s.placement){
37442             case yes.LEFT:
37443                 s.el.setX(s.resizingEl.getRight());
37444                 break;
37445             case yes.RIGHT:
37446                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37447                 break;
37448             case yes.TOP:
37449                 s.el.setY(s.resizingEl.getBottom());
37450                 break;
37451             case yes.BOTTOM:
37452                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37453                 break;
37454         }
37455     }
37456 };
37457
37458 /**
37459  * Orientation constant - Create a vertical SplitBar
37460  * @static
37461  * @type Number
37462  */
37463 Roo.bootstrap.SplitBar.VERTICAL = 1;
37464
37465 /**
37466  * Orientation constant - Create a horizontal SplitBar
37467  * @static
37468  * @type Number
37469  */
37470 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37471
37472 /**
37473  * Placement constant - The resizing element is to the left of the splitter element
37474  * @static
37475  * @type Number
37476  */
37477 Roo.bootstrap.SplitBar.LEFT = 1;
37478
37479 /**
37480  * Placement constant - The resizing element is to the right of the splitter element
37481  * @static
37482  * @type Number
37483  */
37484 Roo.bootstrap.SplitBar.RIGHT = 2;
37485
37486 /**
37487  * Placement constant - The resizing element is positioned above the splitter element
37488  * @static
37489  * @type Number
37490  */
37491 Roo.bootstrap.SplitBar.TOP = 3;
37492
37493 /**
37494  * Placement constant - The resizing element is positioned under splitter element
37495  * @static
37496  * @type Number
37497  */
37498 Roo.bootstrap.SplitBar.BOTTOM = 4;
37499 Roo.namespace("Roo.bootstrap.layout");/*
37500  * Based on:
37501  * Ext JS Library 1.1.1
37502  * Copyright(c) 2006-2007, Ext JS, LLC.
37503  *
37504  * Originally Released Under LGPL - original licence link has changed is not relivant.
37505  *
37506  * Fork - LGPL
37507  * <script type="text/javascript">
37508  */
37509
37510 /**
37511  * @class Roo.bootstrap.layout.Manager
37512  * @extends Roo.bootstrap.Component
37513  * Base class for layout managers.
37514  */
37515 Roo.bootstrap.layout.Manager = function(config)
37516 {
37517     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37518
37519
37520
37521
37522
37523     /** false to disable window resize monitoring @type Boolean */
37524     this.monitorWindowResize = true;
37525     this.regions = {};
37526     this.addEvents({
37527         /**
37528          * @event layout
37529          * Fires when a layout is performed.
37530          * @param {Roo.LayoutManager} this
37531          */
37532         "layout" : true,
37533         /**
37534          * @event regionresized
37535          * Fires when the user resizes a region.
37536          * @param {Roo.LayoutRegion} region The resized region
37537          * @param {Number} newSize The new size (width for east/west, height for north/south)
37538          */
37539         "regionresized" : true,
37540         /**
37541          * @event regioncollapsed
37542          * Fires when a region is collapsed.
37543          * @param {Roo.LayoutRegion} region The collapsed region
37544          */
37545         "regioncollapsed" : true,
37546         /**
37547          * @event regionexpanded
37548          * Fires when a region is expanded.
37549          * @param {Roo.LayoutRegion} region The expanded region
37550          */
37551         "regionexpanded" : true
37552     });
37553     this.updating = false;
37554
37555     if (config.el) {
37556         this.el = Roo.get(config.el);
37557         this.initEvents();
37558     }
37559
37560 };
37561
37562 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37563
37564
37565     regions : null,
37566
37567     monitorWindowResize : true,
37568
37569
37570     updating : false,
37571
37572
37573     onRender : function(ct, position)
37574     {
37575         if(!this.el){
37576             this.el = Roo.get(ct);
37577             this.initEvents();
37578         }
37579         //this.fireEvent('render',this);
37580     },
37581
37582
37583     initEvents: function()
37584     {
37585
37586
37587         // ie scrollbar fix
37588         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37589             document.body.scroll = "no";
37590         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37591             this.el.position('relative');
37592         }
37593         this.id = this.el.id;
37594         this.el.addClass("roo-layout-container");
37595         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37596         if(this.el.dom != document.body ) {
37597             this.el.on('resize', this.layout,this);
37598             this.el.on('show', this.layout,this);
37599         }
37600
37601     },
37602
37603     /**
37604      * Returns true if this layout is currently being updated
37605      * @return {Boolean}
37606      */
37607     isUpdating : function(){
37608         return this.updating;
37609     },
37610
37611     /**
37612      * Suspend the LayoutManager from doing auto-layouts while
37613      * making multiple add or remove calls
37614      */
37615     beginUpdate : function(){
37616         this.updating = true;
37617     },
37618
37619     /**
37620      * Restore auto-layouts and optionally disable the manager from performing a layout
37621      * @param {Boolean} noLayout true to disable a layout update
37622      */
37623     endUpdate : function(noLayout){
37624         this.updating = false;
37625         if(!noLayout){
37626             this.layout();
37627         }
37628     },
37629
37630     layout: function(){
37631         // abstract...
37632     },
37633
37634     onRegionResized : function(region, newSize){
37635         this.fireEvent("regionresized", region, newSize);
37636         this.layout();
37637     },
37638
37639     onRegionCollapsed : function(region){
37640         this.fireEvent("regioncollapsed", region);
37641     },
37642
37643     onRegionExpanded : function(region){
37644         this.fireEvent("regionexpanded", region);
37645     },
37646
37647     /**
37648      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37649      * performs box-model adjustments.
37650      * @return {Object} The size as an object {width: (the width), height: (the height)}
37651      */
37652     getViewSize : function()
37653     {
37654         var size;
37655         if(this.el.dom != document.body){
37656             size = this.el.getSize();
37657         }else{
37658             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37659         }
37660         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37661         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37662         return size;
37663     },
37664
37665     /**
37666      * Returns the Element this layout is bound to.
37667      * @return {Roo.Element}
37668      */
37669     getEl : function(){
37670         return this.el;
37671     },
37672
37673     /**
37674      * Returns the specified region.
37675      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37676      * @return {Roo.LayoutRegion}
37677      */
37678     getRegion : function(target){
37679         return this.regions[target.toLowerCase()];
37680     },
37681
37682     onWindowResize : function(){
37683         if(this.monitorWindowResize){
37684             this.layout();
37685         }
37686     }
37687 });
37688 /*
37689  * Based on:
37690  * Ext JS Library 1.1.1
37691  * Copyright(c) 2006-2007, Ext JS, LLC.
37692  *
37693  * Originally Released Under LGPL - original licence link has changed is not relivant.
37694  *
37695  * Fork - LGPL
37696  * <script type="text/javascript">
37697  */
37698 /**
37699  * @class Roo.bootstrap.layout.Border
37700  * @extends Roo.bootstrap.layout.Manager
37701  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37702  * please see: examples/bootstrap/nested.html<br><br>
37703  
37704 <b>The container the layout is rendered into can be either the body element or any other element.
37705 If it is not the body element, the container needs to either be an absolute positioned element,
37706 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37707 the container size if it is not the body element.</b>
37708
37709 * @constructor
37710 * Create a new Border
37711 * @param {Object} config Configuration options
37712  */
37713 Roo.bootstrap.layout.Border = function(config){
37714     config = config || {};
37715     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37716     
37717     
37718     
37719     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37720         if(config[region]){
37721             config[region].region = region;
37722             this.addRegion(config[region]);
37723         }
37724     },this);
37725     
37726 };
37727
37728 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37729
37730 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37731     
37732     parent : false, // this might point to a 'nest' or a ???
37733     
37734     /**
37735      * Creates and adds a new region if it doesn't already exist.
37736      * @param {String} target The target region key (north, south, east, west or center).
37737      * @param {Object} config The regions config object
37738      * @return {BorderLayoutRegion} The new region
37739      */
37740     addRegion : function(config)
37741     {
37742         if(!this.regions[config.region]){
37743             var r = this.factory(config);
37744             this.bindRegion(r);
37745         }
37746         return this.regions[config.region];
37747     },
37748
37749     // private (kinda)
37750     bindRegion : function(r){
37751         this.regions[r.config.region] = r;
37752         
37753         r.on("visibilitychange",    this.layout, this);
37754         r.on("paneladded",          this.layout, this);
37755         r.on("panelremoved",        this.layout, this);
37756         r.on("invalidated",         this.layout, this);
37757         r.on("resized",             this.onRegionResized, this);
37758         r.on("collapsed",           this.onRegionCollapsed, this);
37759         r.on("expanded",            this.onRegionExpanded, this);
37760     },
37761
37762     /**
37763      * Performs a layout update.
37764      */
37765     layout : function()
37766     {
37767         if(this.updating) {
37768             return;
37769         }
37770         
37771         // render all the rebions if they have not been done alreayd?
37772         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37773             if(this.regions[region] && !this.regions[region].bodyEl){
37774                 this.regions[region].onRender(this.el)
37775             }
37776         },this);
37777         
37778         var size = this.getViewSize();
37779         var w = size.width;
37780         var h = size.height;
37781         var centerW = w;
37782         var centerH = h;
37783         var centerY = 0;
37784         var centerX = 0;
37785         //var x = 0, y = 0;
37786
37787         var rs = this.regions;
37788         var north = rs["north"];
37789         var south = rs["south"]; 
37790         var west = rs["west"];
37791         var east = rs["east"];
37792         var center = rs["center"];
37793         //if(this.hideOnLayout){ // not supported anymore
37794             //c.el.setStyle("display", "none");
37795         //}
37796         if(north && north.isVisible()){
37797             var b = north.getBox();
37798             var m = north.getMargins();
37799             b.width = w - (m.left+m.right);
37800             b.x = m.left;
37801             b.y = m.top;
37802             centerY = b.height + b.y + m.bottom;
37803             centerH -= centerY;
37804             north.updateBox(this.safeBox(b));
37805         }
37806         if(south && south.isVisible()){
37807             var b = south.getBox();
37808             var m = south.getMargins();
37809             b.width = w - (m.left+m.right);
37810             b.x = m.left;
37811             var totalHeight = (b.height + m.top + m.bottom);
37812             b.y = h - totalHeight + m.top;
37813             centerH -= totalHeight;
37814             south.updateBox(this.safeBox(b));
37815         }
37816         if(west && west.isVisible()){
37817             var b = west.getBox();
37818             var m = west.getMargins();
37819             b.height = centerH - (m.top+m.bottom);
37820             b.x = m.left;
37821             b.y = centerY + m.top;
37822             var totalWidth = (b.width + m.left + m.right);
37823             centerX += totalWidth;
37824             centerW -= totalWidth;
37825             west.updateBox(this.safeBox(b));
37826         }
37827         if(east && east.isVisible()){
37828             var b = east.getBox();
37829             var m = east.getMargins();
37830             b.height = centerH - (m.top+m.bottom);
37831             var totalWidth = (b.width + m.left + m.right);
37832             b.x = w - totalWidth + m.left;
37833             b.y = centerY + m.top;
37834             centerW -= totalWidth;
37835             east.updateBox(this.safeBox(b));
37836         }
37837         if(center){
37838             var m = center.getMargins();
37839             var centerBox = {
37840                 x: centerX + m.left,
37841                 y: centerY + m.top,
37842                 width: centerW - (m.left+m.right),
37843                 height: centerH - (m.top+m.bottom)
37844             };
37845             //if(this.hideOnLayout){
37846                 //center.el.setStyle("display", "block");
37847             //}
37848             center.updateBox(this.safeBox(centerBox));
37849         }
37850         this.el.repaint();
37851         this.fireEvent("layout", this);
37852     },
37853
37854     // private
37855     safeBox : function(box){
37856         box.width = Math.max(0, box.width);
37857         box.height = Math.max(0, box.height);
37858         return box;
37859     },
37860
37861     /**
37862      * Adds a ContentPanel (or subclass) to this layout.
37863      * @param {String} target The target region key (north, south, east, west or center).
37864      * @param {Roo.ContentPanel} panel The panel to add
37865      * @return {Roo.ContentPanel} The added panel
37866      */
37867     add : function(target, panel){
37868          
37869         target = target.toLowerCase();
37870         return this.regions[target].add(panel);
37871     },
37872
37873     /**
37874      * Remove a ContentPanel (or subclass) to this layout.
37875      * @param {String} target The target region key (north, south, east, west or center).
37876      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37877      * @return {Roo.ContentPanel} The removed panel
37878      */
37879     remove : function(target, panel){
37880         target = target.toLowerCase();
37881         return this.regions[target].remove(panel);
37882     },
37883
37884     /**
37885      * Searches all regions for a panel with the specified id
37886      * @param {String} panelId
37887      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37888      */
37889     findPanel : function(panelId){
37890         var rs = this.regions;
37891         for(var target in rs){
37892             if(typeof rs[target] != "function"){
37893                 var p = rs[target].getPanel(panelId);
37894                 if(p){
37895                     return p;
37896                 }
37897             }
37898         }
37899         return null;
37900     },
37901
37902     /**
37903      * Searches all regions for a panel with the specified id and activates (shows) it.
37904      * @param {String/ContentPanel} panelId The panels id or the panel itself
37905      * @return {Roo.ContentPanel} The shown panel or null
37906      */
37907     showPanel : function(panelId) {
37908       var rs = this.regions;
37909       for(var target in rs){
37910          var r = rs[target];
37911          if(typeof r != "function"){
37912             if(r.hasPanel(panelId)){
37913                return r.showPanel(panelId);
37914             }
37915          }
37916       }
37917       return null;
37918    },
37919
37920    /**
37921      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37922      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37923      */
37924    /*
37925     restoreState : function(provider){
37926         if(!provider){
37927             provider = Roo.state.Manager;
37928         }
37929         var sm = new Roo.LayoutStateManager();
37930         sm.init(this, provider);
37931     },
37932 */
37933  
37934  
37935     /**
37936      * Adds a xtype elements to the layout.
37937      * <pre><code>
37938
37939 layout.addxtype({
37940        xtype : 'ContentPanel',
37941        region: 'west',
37942        items: [ .... ]
37943    }
37944 );
37945
37946 layout.addxtype({
37947         xtype : 'NestedLayoutPanel',
37948         region: 'west',
37949         layout: {
37950            center: { },
37951            west: { }   
37952         },
37953         items : [ ... list of content panels or nested layout panels.. ]
37954    }
37955 );
37956 </code></pre>
37957      * @param {Object} cfg Xtype definition of item to add.
37958      */
37959     addxtype : function(cfg)
37960     {
37961         // basically accepts a pannel...
37962         // can accept a layout region..!?!?
37963         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37964         
37965         
37966         // theory?  children can only be panels??
37967         
37968         //if (!cfg.xtype.match(/Panel$/)) {
37969         //    return false;
37970         //}
37971         var ret = false;
37972         
37973         if (typeof(cfg.region) == 'undefined') {
37974             Roo.log("Failed to add Panel, region was not set");
37975             Roo.log(cfg);
37976             return false;
37977         }
37978         var region = cfg.region;
37979         delete cfg.region;
37980         
37981           
37982         var xitems = [];
37983         if (cfg.items) {
37984             xitems = cfg.items;
37985             delete cfg.items;
37986         }
37987         var nb = false;
37988         
37989         if ( region == 'center') {
37990             Roo.log("Center: " + cfg.title);
37991         }
37992         
37993         
37994         switch(cfg.xtype) 
37995         {
37996             case 'Content':  // ContentPanel (el, cfg)
37997             case 'Scroll':  // ContentPanel (el, cfg)
37998             case 'View': 
37999                 cfg.autoCreate = cfg.autoCreate || true;
38000                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38001                 //} else {
38002                 //    var el = this.el.createChild();
38003                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38004                 //}
38005                 
38006                 this.add(region, ret);
38007                 break;
38008             
38009             /*
38010             case 'TreePanel': // our new panel!
38011                 cfg.el = this.el.createChild();
38012                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38013                 this.add(region, ret);
38014                 break;
38015             */
38016             
38017             case 'Nest': 
38018                 // create a new Layout (which is  a Border Layout...
38019                 
38020                 var clayout = cfg.layout;
38021                 clayout.el  = this.el.createChild();
38022                 clayout.items   = clayout.items  || [];
38023                 
38024                 delete cfg.layout;
38025                 
38026                 // replace this exitems with the clayout ones..
38027                 xitems = clayout.items;
38028                  
38029                 // force background off if it's in center...
38030                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38031                     cfg.background = false;
38032                 }
38033                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38034                 
38035                 
38036                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38037                 //console.log('adding nested layout panel '  + cfg.toSource());
38038                 this.add(region, ret);
38039                 nb = {}; /// find first...
38040                 break;
38041             
38042             case 'Grid':
38043                 
38044                 // needs grid and region
38045                 
38046                 //var el = this.getRegion(region).el.createChild();
38047                 /*
38048                  *var el = this.el.createChild();
38049                 // create the grid first...
38050                 cfg.grid.container = el;
38051                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38052                 */
38053                 
38054                 if (region == 'center' && this.active ) {
38055                     cfg.background = false;
38056                 }
38057                 
38058                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38059                 
38060                 this.add(region, ret);
38061                 /*
38062                 if (cfg.background) {
38063                     // render grid on panel activation (if panel background)
38064                     ret.on('activate', function(gp) {
38065                         if (!gp.grid.rendered) {
38066                     //        gp.grid.render(el);
38067                         }
38068                     });
38069                 } else {
38070                   //  cfg.grid.render(el);
38071                 }
38072                 */
38073                 break;
38074            
38075            
38076             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38077                 // it was the old xcomponent building that caused this before.
38078                 // espeically if border is the top element in the tree.
38079                 ret = this;
38080                 break; 
38081                 
38082                     
38083                 
38084                 
38085                 
38086             default:
38087                 /*
38088                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38089                     
38090                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38091                     this.add(region, ret);
38092                 } else {
38093                 */
38094                     Roo.log(cfg);
38095                     throw "Can not add '" + cfg.xtype + "' to Border";
38096                     return null;
38097              
38098                                 
38099              
38100         }
38101         this.beginUpdate();
38102         // add children..
38103         var region = '';
38104         var abn = {};
38105         Roo.each(xitems, function(i)  {
38106             region = nb && i.region ? i.region : false;
38107             
38108             var add = ret.addxtype(i);
38109            
38110             if (region) {
38111                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38112                 if (!i.background) {
38113                     abn[region] = nb[region] ;
38114                 }
38115             }
38116             
38117         });
38118         this.endUpdate();
38119
38120         // make the last non-background panel active..
38121         //if (nb) { Roo.log(abn); }
38122         if (nb) {
38123             
38124             for(var r in abn) {
38125                 region = this.getRegion(r);
38126                 if (region) {
38127                     // tried using nb[r], but it does not work..
38128                      
38129                     region.showPanel(abn[r]);
38130                    
38131                 }
38132             }
38133         }
38134         return ret;
38135         
38136     },
38137     
38138     
38139 // private
38140     factory : function(cfg)
38141     {
38142         
38143         var validRegions = Roo.bootstrap.layout.Border.regions;
38144
38145         var target = cfg.region;
38146         cfg.mgr = this;
38147         
38148         var r = Roo.bootstrap.layout;
38149         Roo.log(target);
38150         switch(target){
38151             case "north":
38152                 return new r.North(cfg);
38153             case "south":
38154                 return new r.South(cfg);
38155             case "east":
38156                 return new r.East(cfg);
38157             case "west":
38158                 return new r.West(cfg);
38159             case "center":
38160                 return new r.Center(cfg);
38161         }
38162         throw 'Layout region "'+target+'" not supported.';
38163     }
38164     
38165     
38166 });
38167  /*
38168  * Based on:
38169  * Ext JS Library 1.1.1
38170  * Copyright(c) 2006-2007, Ext JS, LLC.
38171  *
38172  * Originally Released Under LGPL - original licence link has changed is not relivant.
38173  *
38174  * Fork - LGPL
38175  * <script type="text/javascript">
38176  */
38177  
38178 /**
38179  * @class Roo.bootstrap.layout.Basic
38180  * @extends Roo.util.Observable
38181  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38182  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38183  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38184  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38185  * @cfg {string}   region  the region that it inhabits..
38186  * @cfg {bool}   skipConfig skip config?
38187  * 
38188
38189  */
38190 Roo.bootstrap.layout.Basic = function(config){
38191     
38192     this.mgr = config.mgr;
38193     
38194     this.position = config.region;
38195     
38196     var skipConfig = config.skipConfig;
38197     
38198     this.events = {
38199         /**
38200          * @scope Roo.BasicLayoutRegion
38201          */
38202         
38203         /**
38204          * @event beforeremove
38205          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38206          * @param {Roo.LayoutRegion} this
38207          * @param {Roo.ContentPanel} panel The panel
38208          * @param {Object} e The cancel event object
38209          */
38210         "beforeremove" : true,
38211         /**
38212          * @event invalidated
38213          * Fires when the layout for this region is changed.
38214          * @param {Roo.LayoutRegion} this
38215          */
38216         "invalidated" : true,
38217         /**
38218          * @event visibilitychange
38219          * Fires when this region is shown or hidden 
38220          * @param {Roo.LayoutRegion} this
38221          * @param {Boolean} visibility true or false
38222          */
38223         "visibilitychange" : true,
38224         /**
38225          * @event paneladded
38226          * Fires when a panel is added. 
38227          * @param {Roo.LayoutRegion} this
38228          * @param {Roo.ContentPanel} panel The panel
38229          */
38230         "paneladded" : true,
38231         /**
38232          * @event panelremoved
38233          * Fires when a panel is removed. 
38234          * @param {Roo.LayoutRegion} this
38235          * @param {Roo.ContentPanel} panel The panel
38236          */
38237         "panelremoved" : true,
38238         /**
38239          * @event beforecollapse
38240          * Fires when this region before collapse.
38241          * @param {Roo.LayoutRegion} this
38242          */
38243         "beforecollapse" : true,
38244         /**
38245          * @event collapsed
38246          * Fires when this region is collapsed.
38247          * @param {Roo.LayoutRegion} this
38248          */
38249         "collapsed" : true,
38250         /**
38251          * @event expanded
38252          * Fires when this region is expanded.
38253          * @param {Roo.LayoutRegion} this
38254          */
38255         "expanded" : true,
38256         /**
38257          * @event slideshow
38258          * Fires when this region is slid into view.
38259          * @param {Roo.LayoutRegion} this
38260          */
38261         "slideshow" : true,
38262         /**
38263          * @event slidehide
38264          * Fires when this region slides out of view. 
38265          * @param {Roo.LayoutRegion} this
38266          */
38267         "slidehide" : true,
38268         /**
38269          * @event panelactivated
38270          * Fires when a panel is activated. 
38271          * @param {Roo.LayoutRegion} this
38272          * @param {Roo.ContentPanel} panel The activated panel
38273          */
38274         "panelactivated" : true,
38275         /**
38276          * @event resized
38277          * Fires when the user resizes this region. 
38278          * @param {Roo.LayoutRegion} this
38279          * @param {Number} newSize The new size (width for east/west, height for north/south)
38280          */
38281         "resized" : true
38282     };
38283     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38284     this.panels = new Roo.util.MixedCollection();
38285     this.panels.getKey = this.getPanelId.createDelegate(this);
38286     this.box = null;
38287     this.activePanel = null;
38288     // ensure listeners are added...
38289     
38290     if (config.listeners || config.events) {
38291         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38292             listeners : config.listeners || {},
38293             events : config.events || {}
38294         });
38295     }
38296     
38297     if(skipConfig !== true){
38298         this.applyConfig(config);
38299     }
38300 };
38301
38302 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38303 {
38304     getPanelId : function(p){
38305         return p.getId();
38306     },
38307     
38308     applyConfig : function(config){
38309         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38310         this.config = config;
38311         
38312     },
38313     
38314     /**
38315      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38316      * the width, for horizontal (north, south) the height.
38317      * @param {Number} newSize The new width or height
38318      */
38319     resizeTo : function(newSize){
38320         var el = this.el ? this.el :
38321                  (this.activePanel ? this.activePanel.getEl() : null);
38322         if(el){
38323             switch(this.position){
38324                 case "east":
38325                 case "west":
38326                     el.setWidth(newSize);
38327                     this.fireEvent("resized", this, newSize);
38328                 break;
38329                 case "north":
38330                 case "south":
38331                     el.setHeight(newSize);
38332                     this.fireEvent("resized", this, newSize);
38333                 break;                
38334             }
38335         }
38336     },
38337     
38338     getBox : function(){
38339         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38340     },
38341     
38342     getMargins : function(){
38343         return this.margins;
38344     },
38345     
38346     updateBox : function(box){
38347         this.box = box;
38348         var el = this.activePanel.getEl();
38349         el.dom.style.left = box.x + "px";
38350         el.dom.style.top = box.y + "px";
38351         this.activePanel.setSize(box.width, box.height);
38352     },
38353     
38354     /**
38355      * Returns the container element for this region.
38356      * @return {Roo.Element}
38357      */
38358     getEl : function(){
38359         return this.activePanel;
38360     },
38361     
38362     /**
38363      * Returns true if this region is currently visible.
38364      * @return {Boolean}
38365      */
38366     isVisible : function(){
38367         return this.activePanel ? true : false;
38368     },
38369     
38370     setActivePanel : function(panel){
38371         panel = this.getPanel(panel);
38372         if(this.activePanel && this.activePanel != panel){
38373             this.activePanel.setActiveState(false);
38374             this.activePanel.getEl().setLeftTop(-10000,-10000);
38375         }
38376         this.activePanel = panel;
38377         panel.setActiveState(true);
38378         if(this.box){
38379             panel.setSize(this.box.width, this.box.height);
38380         }
38381         this.fireEvent("panelactivated", this, panel);
38382         this.fireEvent("invalidated");
38383     },
38384     
38385     /**
38386      * Show the specified panel.
38387      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38388      * @return {Roo.ContentPanel} The shown panel or null
38389      */
38390     showPanel : function(panel){
38391         panel = this.getPanel(panel);
38392         if(panel){
38393             this.setActivePanel(panel);
38394         }
38395         return panel;
38396     },
38397     
38398     /**
38399      * Get the active panel for this region.
38400      * @return {Roo.ContentPanel} The active panel or null
38401      */
38402     getActivePanel : function(){
38403         return this.activePanel;
38404     },
38405     
38406     /**
38407      * Add the passed ContentPanel(s)
38408      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38409      * @return {Roo.ContentPanel} The panel added (if only one was added)
38410      */
38411     add : function(panel){
38412         if(arguments.length > 1){
38413             for(var i = 0, len = arguments.length; i < len; i++) {
38414                 this.add(arguments[i]);
38415             }
38416             return null;
38417         }
38418         if(this.hasPanel(panel)){
38419             this.showPanel(panel);
38420             return panel;
38421         }
38422         var el = panel.getEl();
38423         if(el.dom.parentNode != this.mgr.el.dom){
38424             this.mgr.el.dom.appendChild(el.dom);
38425         }
38426         if(panel.setRegion){
38427             panel.setRegion(this);
38428         }
38429         this.panels.add(panel);
38430         el.setStyle("position", "absolute");
38431         if(!panel.background){
38432             this.setActivePanel(panel);
38433             if(this.config.initialSize && this.panels.getCount()==1){
38434                 this.resizeTo(this.config.initialSize);
38435             }
38436         }
38437         this.fireEvent("paneladded", this, panel);
38438         return panel;
38439     },
38440     
38441     /**
38442      * Returns true if the panel is in this region.
38443      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38444      * @return {Boolean}
38445      */
38446     hasPanel : function(panel){
38447         if(typeof panel == "object"){ // must be panel obj
38448             panel = panel.getId();
38449         }
38450         return this.getPanel(panel) ? true : false;
38451     },
38452     
38453     /**
38454      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38455      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38456      * @param {Boolean} preservePanel Overrides the config preservePanel option
38457      * @return {Roo.ContentPanel} The panel that was removed
38458      */
38459     remove : function(panel, preservePanel){
38460         panel = this.getPanel(panel);
38461         if(!panel){
38462             return null;
38463         }
38464         var e = {};
38465         this.fireEvent("beforeremove", this, panel, e);
38466         if(e.cancel === true){
38467             return null;
38468         }
38469         var panelId = panel.getId();
38470         this.panels.removeKey(panelId);
38471         return panel;
38472     },
38473     
38474     /**
38475      * Returns the panel specified or null if it's not in this region.
38476      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38477      * @return {Roo.ContentPanel}
38478      */
38479     getPanel : function(id){
38480         if(typeof id == "object"){ // must be panel obj
38481             return id;
38482         }
38483         return this.panels.get(id);
38484     },
38485     
38486     /**
38487      * Returns this regions position (north/south/east/west/center).
38488      * @return {String} 
38489      */
38490     getPosition: function(){
38491         return this.position;    
38492     }
38493 });/*
38494  * Based on:
38495  * Ext JS Library 1.1.1
38496  * Copyright(c) 2006-2007, Ext JS, LLC.
38497  *
38498  * Originally Released Under LGPL - original licence link has changed is not relivant.
38499  *
38500  * Fork - LGPL
38501  * <script type="text/javascript">
38502  */
38503  
38504 /**
38505  * @class Roo.bootstrap.layout.Region
38506  * @extends Roo.bootstrap.layout.Basic
38507  * This class represents a region in a layout manager.
38508  
38509  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38510  * @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})
38511  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38512  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38513  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38514  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38515  * @cfg {String}    title           The title for the region (overrides panel titles)
38516  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38517  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38518  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38519  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38520  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38521  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38522  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38523  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38524  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38525  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38526
38527  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38528  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38529  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38530  * @cfg {Number}    width           For East/West panels
38531  * @cfg {Number}    height          For North/South panels
38532  * @cfg {Boolean}   split           To show the splitter
38533  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38534  * 
38535  * @cfg {string}   cls             Extra CSS classes to add to region
38536  * 
38537  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38538  * @cfg {string}   region  the region that it inhabits..
38539  *
38540
38541  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38542  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38543
38544  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38545  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38546  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38547  */
38548 Roo.bootstrap.layout.Region = function(config)
38549 {
38550     this.applyConfig(config);
38551
38552     var mgr = config.mgr;
38553     var pos = config.region;
38554     config.skipConfig = true;
38555     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38556     
38557     if (mgr.el) {
38558         this.onRender(mgr.el);   
38559     }
38560      
38561     this.visible = true;
38562     this.collapsed = false;
38563     this.unrendered_panels = [];
38564 };
38565
38566 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38567
38568     position: '', // set by wrapper (eg. north/south etc..)
38569     unrendered_panels : null,  // unrendered panels.
38570     
38571     tabPosition : false,
38572     
38573     mgr: false, // points to 'Border'
38574     
38575     
38576     createBody : function(){
38577         /** This region's body element 
38578         * @type Roo.Element */
38579         this.bodyEl = this.el.createChild({
38580                 tag: "div",
38581                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38582         });
38583     },
38584
38585     onRender: function(ctr, pos)
38586     {
38587         var dh = Roo.DomHelper;
38588         /** This region's container element 
38589         * @type Roo.Element */
38590         this.el = dh.append(ctr.dom, {
38591                 tag: "div",
38592                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38593             }, true);
38594         /** This region's title element 
38595         * @type Roo.Element */
38596     
38597         this.titleEl = dh.append(this.el.dom,  {
38598                 tag: "div",
38599                 unselectable: "on",
38600                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38601                 children:[
38602                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38603                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38604                 ]
38605             }, true);
38606         
38607         this.titleEl.enableDisplayMode();
38608         /** This region's title text element 
38609         * @type HTMLElement */
38610         this.titleTextEl = this.titleEl.dom.firstChild;
38611         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38612         /*
38613         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38614         this.closeBtn.enableDisplayMode();
38615         this.closeBtn.on("click", this.closeClicked, this);
38616         this.closeBtn.hide();
38617     */
38618         this.createBody(this.config);
38619         if(this.config.hideWhenEmpty){
38620             this.hide();
38621             this.on("paneladded", this.validateVisibility, this);
38622             this.on("panelremoved", this.validateVisibility, this);
38623         }
38624         if(this.autoScroll){
38625             this.bodyEl.setStyle("overflow", "auto");
38626         }else{
38627             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38628         }
38629         //if(c.titlebar !== false){
38630             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38631                 this.titleEl.hide();
38632             }else{
38633                 this.titleEl.show();
38634                 if(this.config.title){
38635                     this.titleTextEl.innerHTML = this.config.title;
38636                 }
38637             }
38638         //}
38639         if(this.config.collapsed){
38640             this.collapse(true);
38641         }
38642         if(this.config.hidden){
38643             this.hide();
38644         }
38645         
38646         if (this.unrendered_panels && this.unrendered_panels.length) {
38647             for (var i =0;i< this.unrendered_panels.length; i++) {
38648                 this.add(this.unrendered_panels[i]);
38649             }
38650             this.unrendered_panels = null;
38651             
38652         }
38653         
38654     },
38655     
38656     applyConfig : function(c)
38657     {
38658         /*
38659          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38660             var dh = Roo.DomHelper;
38661             if(c.titlebar !== false){
38662                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38663                 this.collapseBtn.on("click", this.collapse, this);
38664                 this.collapseBtn.enableDisplayMode();
38665                 /*
38666                 if(c.showPin === true || this.showPin){
38667                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38668                     this.stickBtn.enableDisplayMode();
38669                     this.stickBtn.on("click", this.expand, this);
38670                     this.stickBtn.hide();
38671                 }
38672                 
38673             }
38674             */
38675             /** This region's collapsed element
38676             * @type Roo.Element */
38677             /*
38678              *
38679             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38680                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38681             ]}, true);
38682             
38683             if(c.floatable !== false){
38684                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38685                this.collapsedEl.on("click", this.collapseClick, this);
38686             }
38687
38688             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38689                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38690                    id: "message", unselectable: "on", style:{"float":"left"}});
38691                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38692              }
38693             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38694             this.expandBtn.on("click", this.expand, this);
38695             
38696         }
38697         
38698         if(this.collapseBtn){
38699             this.collapseBtn.setVisible(c.collapsible == true);
38700         }
38701         
38702         this.cmargins = c.cmargins || this.cmargins ||
38703                          (this.position == "west" || this.position == "east" ?
38704                              {top: 0, left: 2, right:2, bottom: 0} :
38705                              {top: 2, left: 0, right:0, bottom: 2});
38706         */
38707         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38708         
38709         
38710         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38711         
38712         this.autoScroll = c.autoScroll || false;
38713         
38714         
38715        
38716         
38717         this.duration = c.duration || .30;
38718         this.slideDuration = c.slideDuration || .45;
38719         this.config = c;
38720        
38721     },
38722     /**
38723      * Returns true if this region is currently visible.
38724      * @return {Boolean}
38725      */
38726     isVisible : function(){
38727         return this.visible;
38728     },
38729
38730     /**
38731      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38732      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38733      */
38734     //setCollapsedTitle : function(title){
38735     //    title = title || "&#160;";
38736      //   if(this.collapsedTitleTextEl){
38737       //      this.collapsedTitleTextEl.innerHTML = title;
38738        // }
38739     //},
38740
38741     getBox : function(){
38742         var b;
38743       //  if(!this.collapsed){
38744             b = this.el.getBox(false, true);
38745        // }else{
38746           //  b = this.collapsedEl.getBox(false, true);
38747         //}
38748         return b;
38749     },
38750
38751     getMargins : function(){
38752         return this.margins;
38753         //return this.collapsed ? this.cmargins : this.margins;
38754     },
38755 /*
38756     highlight : function(){
38757         this.el.addClass("x-layout-panel-dragover");
38758     },
38759
38760     unhighlight : function(){
38761         this.el.removeClass("x-layout-panel-dragover");
38762     },
38763 */
38764     updateBox : function(box)
38765     {
38766         if (!this.bodyEl) {
38767             return; // not rendered yet..
38768         }
38769         
38770         this.box = box;
38771         if(!this.collapsed){
38772             this.el.dom.style.left = box.x + "px";
38773             this.el.dom.style.top = box.y + "px";
38774             this.updateBody(box.width, box.height);
38775         }else{
38776             this.collapsedEl.dom.style.left = box.x + "px";
38777             this.collapsedEl.dom.style.top = box.y + "px";
38778             this.collapsedEl.setSize(box.width, box.height);
38779         }
38780         if(this.tabs){
38781             this.tabs.autoSizeTabs();
38782         }
38783     },
38784
38785     updateBody : function(w, h)
38786     {
38787         if(w !== null){
38788             this.el.setWidth(w);
38789             w -= this.el.getBorderWidth("rl");
38790             if(this.config.adjustments){
38791                 w += this.config.adjustments[0];
38792             }
38793         }
38794         if(h !== null && h > 0){
38795             this.el.setHeight(h);
38796             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38797             h -= this.el.getBorderWidth("tb");
38798             if(this.config.adjustments){
38799                 h += this.config.adjustments[1];
38800             }
38801             this.bodyEl.setHeight(h);
38802             if(this.tabs){
38803                 h = this.tabs.syncHeight(h);
38804             }
38805         }
38806         if(this.panelSize){
38807             w = w !== null ? w : this.panelSize.width;
38808             h = h !== null ? h : this.panelSize.height;
38809         }
38810         if(this.activePanel){
38811             var el = this.activePanel.getEl();
38812             w = w !== null ? w : el.getWidth();
38813             h = h !== null ? h : el.getHeight();
38814             this.panelSize = {width: w, height: h};
38815             this.activePanel.setSize(w, h);
38816         }
38817         if(Roo.isIE && this.tabs){
38818             this.tabs.el.repaint();
38819         }
38820     },
38821
38822     /**
38823      * Returns the container element for this region.
38824      * @return {Roo.Element}
38825      */
38826     getEl : function(){
38827         return this.el;
38828     },
38829
38830     /**
38831      * Hides this region.
38832      */
38833     hide : function(){
38834         //if(!this.collapsed){
38835             this.el.dom.style.left = "-2000px";
38836             this.el.hide();
38837         //}else{
38838          //   this.collapsedEl.dom.style.left = "-2000px";
38839          //   this.collapsedEl.hide();
38840        // }
38841         this.visible = false;
38842         this.fireEvent("visibilitychange", this, false);
38843     },
38844
38845     /**
38846      * Shows this region if it was previously hidden.
38847      */
38848     show : function(){
38849         //if(!this.collapsed){
38850             this.el.show();
38851         //}else{
38852         //    this.collapsedEl.show();
38853        // }
38854         this.visible = true;
38855         this.fireEvent("visibilitychange", this, true);
38856     },
38857 /*
38858     closeClicked : function(){
38859         if(this.activePanel){
38860             this.remove(this.activePanel);
38861         }
38862     },
38863
38864     collapseClick : function(e){
38865         if(this.isSlid){
38866            e.stopPropagation();
38867            this.slideIn();
38868         }else{
38869            e.stopPropagation();
38870            this.slideOut();
38871         }
38872     },
38873 */
38874     /**
38875      * Collapses this region.
38876      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38877      */
38878     /*
38879     collapse : function(skipAnim, skipCheck = false){
38880         if(this.collapsed) {
38881             return;
38882         }
38883         
38884         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38885             
38886             this.collapsed = true;
38887             if(this.split){
38888                 this.split.el.hide();
38889             }
38890             if(this.config.animate && skipAnim !== true){
38891                 this.fireEvent("invalidated", this);
38892                 this.animateCollapse();
38893             }else{
38894                 this.el.setLocation(-20000,-20000);
38895                 this.el.hide();
38896                 this.collapsedEl.show();
38897                 this.fireEvent("collapsed", this);
38898                 this.fireEvent("invalidated", this);
38899             }
38900         }
38901         
38902     },
38903 */
38904     animateCollapse : function(){
38905         // overridden
38906     },
38907
38908     /**
38909      * Expands this region if it was previously collapsed.
38910      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38911      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38912      */
38913     /*
38914     expand : function(e, skipAnim){
38915         if(e) {
38916             e.stopPropagation();
38917         }
38918         if(!this.collapsed || this.el.hasActiveFx()) {
38919             return;
38920         }
38921         if(this.isSlid){
38922             this.afterSlideIn();
38923             skipAnim = true;
38924         }
38925         this.collapsed = false;
38926         if(this.config.animate && skipAnim !== true){
38927             this.animateExpand();
38928         }else{
38929             this.el.show();
38930             if(this.split){
38931                 this.split.el.show();
38932             }
38933             this.collapsedEl.setLocation(-2000,-2000);
38934             this.collapsedEl.hide();
38935             this.fireEvent("invalidated", this);
38936             this.fireEvent("expanded", this);
38937         }
38938     },
38939 */
38940     animateExpand : function(){
38941         // overridden
38942     },
38943
38944     initTabs : function()
38945     {
38946         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38947         
38948         var ts = new Roo.bootstrap.panel.Tabs({
38949             el: this.bodyEl.dom,
38950             region : this,
38951             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38952             disableTooltips: this.config.disableTabTips,
38953             toolbar : this.config.toolbar
38954         });
38955         
38956         if(this.config.hideTabs){
38957             ts.stripWrap.setDisplayed(false);
38958         }
38959         this.tabs = ts;
38960         ts.resizeTabs = this.config.resizeTabs === true;
38961         ts.minTabWidth = this.config.minTabWidth || 40;
38962         ts.maxTabWidth = this.config.maxTabWidth || 250;
38963         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38964         ts.monitorResize = false;
38965         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38966         ts.bodyEl.addClass('roo-layout-tabs-body');
38967         this.panels.each(this.initPanelAsTab, this);
38968     },
38969
38970     initPanelAsTab : function(panel){
38971         var ti = this.tabs.addTab(
38972             panel.getEl().id,
38973             panel.getTitle(),
38974             null,
38975             this.config.closeOnTab && panel.isClosable(),
38976             panel.tpl
38977         );
38978         if(panel.tabTip !== undefined){
38979             ti.setTooltip(panel.tabTip);
38980         }
38981         ti.on("activate", function(){
38982               this.setActivePanel(panel);
38983         }, this);
38984         
38985         if(this.config.closeOnTab){
38986             ti.on("beforeclose", function(t, e){
38987                 e.cancel = true;
38988                 this.remove(panel);
38989             }, this);
38990         }
38991         
38992         panel.tabItem = ti;
38993         
38994         return ti;
38995     },
38996
38997     updatePanelTitle : function(panel, title)
38998     {
38999         if(this.activePanel == panel){
39000             this.updateTitle(title);
39001         }
39002         if(this.tabs){
39003             var ti = this.tabs.getTab(panel.getEl().id);
39004             ti.setText(title);
39005             if(panel.tabTip !== undefined){
39006                 ti.setTooltip(panel.tabTip);
39007             }
39008         }
39009     },
39010
39011     updateTitle : function(title){
39012         if(this.titleTextEl && !this.config.title){
39013             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39014         }
39015     },
39016
39017     setActivePanel : function(panel)
39018     {
39019         panel = this.getPanel(panel);
39020         if(this.activePanel && this.activePanel != panel){
39021             if(this.activePanel.setActiveState(false) === false){
39022                 return;
39023             }
39024         }
39025         this.activePanel = panel;
39026         panel.setActiveState(true);
39027         if(this.panelSize){
39028             panel.setSize(this.panelSize.width, this.panelSize.height);
39029         }
39030         if(this.closeBtn){
39031             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39032         }
39033         this.updateTitle(panel.getTitle());
39034         if(this.tabs){
39035             this.fireEvent("invalidated", this);
39036         }
39037         this.fireEvent("panelactivated", this, panel);
39038     },
39039
39040     /**
39041      * Shows the specified panel.
39042      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39043      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39044      */
39045     showPanel : function(panel)
39046     {
39047         panel = this.getPanel(panel);
39048         if(panel){
39049             if(this.tabs){
39050                 var tab = this.tabs.getTab(panel.getEl().id);
39051                 if(tab.isHidden()){
39052                     this.tabs.unhideTab(tab.id);
39053                 }
39054                 tab.activate();
39055             }else{
39056                 this.setActivePanel(panel);
39057             }
39058         }
39059         return panel;
39060     },
39061
39062     /**
39063      * Get the active panel for this region.
39064      * @return {Roo.ContentPanel} The active panel or null
39065      */
39066     getActivePanel : function(){
39067         return this.activePanel;
39068     },
39069
39070     validateVisibility : function(){
39071         if(this.panels.getCount() < 1){
39072             this.updateTitle("&#160;");
39073             this.closeBtn.hide();
39074             this.hide();
39075         }else{
39076             if(!this.isVisible()){
39077                 this.show();
39078             }
39079         }
39080     },
39081
39082     /**
39083      * Adds the passed ContentPanel(s) to this region.
39084      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39085      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39086      */
39087     add : function(panel)
39088     {
39089         if(arguments.length > 1){
39090             for(var i = 0, len = arguments.length; i < len; i++) {
39091                 this.add(arguments[i]);
39092             }
39093             return null;
39094         }
39095         
39096         // if we have not been rendered yet, then we can not really do much of this..
39097         if (!this.bodyEl) {
39098             this.unrendered_panels.push(panel);
39099             return panel;
39100         }
39101         
39102         
39103         
39104         
39105         if(this.hasPanel(panel)){
39106             this.showPanel(panel);
39107             return panel;
39108         }
39109         panel.setRegion(this);
39110         this.panels.add(panel);
39111        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39112             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39113             // and hide them... ???
39114             this.bodyEl.dom.appendChild(panel.getEl().dom);
39115             if(panel.background !== true){
39116                 this.setActivePanel(panel);
39117             }
39118             this.fireEvent("paneladded", this, panel);
39119             return panel;
39120         }
39121         */
39122         if(!this.tabs){
39123             this.initTabs();
39124         }else{
39125             this.initPanelAsTab(panel);
39126         }
39127         
39128         
39129         if(panel.background !== true){
39130             this.tabs.activate(panel.getEl().id);
39131         }
39132         this.fireEvent("paneladded", this, panel);
39133         return panel;
39134     },
39135
39136     /**
39137      * Hides the tab for the specified panel.
39138      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39139      */
39140     hidePanel : function(panel){
39141         if(this.tabs && (panel = this.getPanel(panel))){
39142             this.tabs.hideTab(panel.getEl().id);
39143         }
39144     },
39145
39146     /**
39147      * Unhides the tab for a previously hidden panel.
39148      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39149      */
39150     unhidePanel : function(panel){
39151         if(this.tabs && (panel = this.getPanel(panel))){
39152             this.tabs.unhideTab(panel.getEl().id);
39153         }
39154     },
39155
39156     clearPanels : function(){
39157         while(this.panels.getCount() > 0){
39158              this.remove(this.panels.first());
39159         }
39160     },
39161
39162     /**
39163      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39164      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39165      * @param {Boolean} preservePanel Overrides the config preservePanel option
39166      * @return {Roo.ContentPanel} The panel that was removed
39167      */
39168     remove : function(panel, preservePanel)
39169     {
39170         panel = this.getPanel(panel);
39171         if(!panel){
39172             return null;
39173         }
39174         var e = {};
39175         this.fireEvent("beforeremove", this, panel, e);
39176         if(e.cancel === true){
39177             return null;
39178         }
39179         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39180         var panelId = panel.getId();
39181         this.panels.removeKey(panelId);
39182         if(preservePanel){
39183             document.body.appendChild(panel.getEl().dom);
39184         }
39185         if(this.tabs){
39186             this.tabs.removeTab(panel.getEl().id);
39187         }else if (!preservePanel){
39188             this.bodyEl.dom.removeChild(panel.getEl().dom);
39189         }
39190         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39191             var p = this.panels.first();
39192             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39193             tempEl.appendChild(p.getEl().dom);
39194             this.bodyEl.update("");
39195             this.bodyEl.dom.appendChild(p.getEl().dom);
39196             tempEl = null;
39197             this.updateTitle(p.getTitle());
39198             this.tabs = null;
39199             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39200             this.setActivePanel(p);
39201         }
39202         panel.setRegion(null);
39203         if(this.activePanel == panel){
39204             this.activePanel = null;
39205         }
39206         if(this.config.autoDestroy !== false && preservePanel !== true){
39207             try{panel.destroy();}catch(e){}
39208         }
39209         this.fireEvent("panelremoved", this, panel);
39210         return panel;
39211     },
39212
39213     /**
39214      * Returns the TabPanel component used by this region
39215      * @return {Roo.TabPanel}
39216      */
39217     getTabs : function(){
39218         return this.tabs;
39219     },
39220
39221     createTool : function(parentEl, className){
39222         var btn = Roo.DomHelper.append(parentEl, {
39223             tag: "div",
39224             cls: "x-layout-tools-button",
39225             children: [ {
39226                 tag: "div",
39227                 cls: "roo-layout-tools-button-inner " + className,
39228                 html: "&#160;"
39229             }]
39230         }, true);
39231         btn.addClassOnOver("roo-layout-tools-button-over");
39232         return btn;
39233     }
39234 });/*
39235  * Based on:
39236  * Ext JS Library 1.1.1
39237  * Copyright(c) 2006-2007, Ext JS, LLC.
39238  *
39239  * Originally Released Under LGPL - original licence link has changed is not relivant.
39240  *
39241  * Fork - LGPL
39242  * <script type="text/javascript">
39243  */
39244  
39245
39246
39247 /**
39248  * @class Roo.SplitLayoutRegion
39249  * @extends Roo.LayoutRegion
39250  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39251  */
39252 Roo.bootstrap.layout.Split = function(config){
39253     this.cursor = config.cursor;
39254     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39255 };
39256
39257 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39258 {
39259     splitTip : "Drag to resize.",
39260     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39261     useSplitTips : false,
39262
39263     applyConfig : function(config){
39264         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39265     },
39266     
39267     onRender : function(ctr,pos) {
39268         
39269         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39270         if(!this.config.split){
39271             return;
39272         }
39273         if(!this.split){
39274             
39275             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39276                             tag: "div",
39277                             id: this.el.id + "-split",
39278                             cls: "roo-layout-split roo-layout-split-"+this.position,
39279                             html: "&#160;"
39280             });
39281             /** The SplitBar for this region 
39282             * @type Roo.SplitBar */
39283             // does not exist yet...
39284             Roo.log([this.position, this.orientation]);
39285             
39286             this.split = new Roo.bootstrap.SplitBar({
39287                 dragElement : splitEl,
39288                 resizingElement: this.el,
39289                 orientation : this.orientation
39290             });
39291             
39292             this.split.on("moved", this.onSplitMove, this);
39293             this.split.useShim = this.config.useShim === true;
39294             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39295             if(this.useSplitTips){
39296                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39297             }
39298             //if(config.collapsible){
39299             //    this.split.el.on("dblclick", this.collapse,  this);
39300             //}
39301         }
39302         if(typeof this.config.minSize != "undefined"){
39303             this.split.minSize = this.config.minSize;
39304         }
39305         if(typeof this.config.maxSize != "undefined"){
39306             this.split.maxSize = this.config.maxSize;
39307         }
39308         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39309             this.hideSplitter();
39310         }
39311         
39312     },
39313
39314     getHMaxSize : function(){
39315          var cmax = this.config.maxSize || 10000;
39316          var center = this.mgr.getRegion("center");
39317          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39318     },
39319
39320     getVMaxSize : function(){
39321          var cmax = this.config.maxSize || 10000;
39322          var center = this.mgr.getRegion("center");
39323          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39324     },
39325
39326     onSplitMove : function(split, newSize){
39327         this.fireEvent("resized", this, newSize);
39328     },
39329     
39330     /** 
39331      * Returns the {@link Roo.SplitBar} for this region.
39332      * @return {Roo.SplitBar}
39333      */
39334     getSplitBar : function(){
39335         return this.split;
39336     },
39337     
39338     hide : function(){
39339         this.hideSplitter();
39340         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39341     },
39342
39343     hideSplitter : function(){
39344         if(this.split){
39345             this.split.el.setLocation(-2000,-2000);
39346             this.split.el.hide();
39347         }
39348     },
39349
39350     show : function(){
39351         if(this.split){
39352             this.split.el.show();
39353         }
39354         Roo.bootstrap.layout.Split.superclass.show.call(this);
39355     },
39356     
39357     beforeSlide: function(){
39358         if(Roo.isGecko){// firefox overflow auto bug workaround
39359             this.bodyEl.clip();
39360             if(this.tabs) {
39361                 this.tabs.bodyEl.clip();
39362             }
39363             if(this.activePanel){
39364                 this.activePanel.getEl().clip();
39365                 
39366                 if(this.activePanel.beforeSlide){
39367                     this.activePanel.beforeSlide();
39368                 }
39369             }
39370         }
39371     },
39372     
39373     afterSlide : function(){
39374         if(Roo.isGecko){// firefox overflow auto bug workaround
39375             this.bodyEl.unclip();
39376             if(this.tabs) {
39377                 this.tabs.bodyEl.unclip();
39378             }
39379             if(this.activePanel){
39380                 this.activePanel.getEl().unclip();
39381                 if(this.activePanel.afterSlide){
39382                     this.activePanel.afterSlide();
39383                 }
39384             }
39385         }
39386     },
39387
39388     initAutoHide : function(){
39389         if(this.autoHide !== false){
39390             if(!this.autoHideHd){
39391                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39392                 this.autoHideHd = {
39393                     "mouseout": function(e){
39394                         if(!e.within(this.el, true)){
39395                             st.delay(500);
39396                         }
39397                     },
39398                     "mouseover" : function(e){
39399                         st.cancel();
39400                     },
39401                     scope : this
39402                 };
39403             }
39404             this.el.on(this.autoHideHd);
39405         }
39406     },
39407
39408     clearAutoHide : function(){
39409         if(this.autoHide !== false){
39410             this.el.un("mouseout", this.autoHideHd.mouseout);
39411             this.el.un("mouseover", this.autoHideHd.mouseover);
39412         }
39413     },
39414
39415     clearMonitor : function(){
39416         Roo.get(document).un("click", this.slideInIf, this);
39417     },
39418
39419     // these names are backwards but not changed for compat
39420     slideOut : function(){
39421         if(this.isSlid || this.el.hasActiveFx()){
39422             return;
39423         }
39424         this.isSlid = true;
39425         if(this.collapseBtn){
39426             this.collapseBtn.hide();
39427         }
39428         this.closeBtnState = this.closeBtn.getStyle('display');
39429         this.closeBtn.hide();
39430         if(this.stickBtn){
39431             this.stickBtn.show();
39432         }
39433         this.el.show();
39434         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39435         this.beforeSlide();
39436         this.el.setStyle("z-index", 10001);
39437         this.el.slideIn(this.getSlideAnchor(), {
39438             callback: function(){
39439                 this.afterSlide();
39440                 this.initAutoHide();
39441                 Roo.get(document).on("click", this.slideInIf, this);
39442                 this.fireEvent("slideshow", this);
39443             },
39444             scope: this,
39445             block: true
39446         });
39447     },
39448
39449     afterSlideIn : function(){
39450         this.clearAutoHide();
39451         this.isSlid = false;
39452         this.clearMonitor();
39453         this.el.setStyle("z-index", "");
39454         if(this.collapseBtn){
39455             this.collapseBtn.show();
39456         }
39457         this.closeBtn.setStyle('display', this.closeBtnState);
39458         if(this.stickBtn){
39459             this.stickBtn.hide();
39460         }
39461         this.fireEvent("slidehide", this);
39462     },
39463
39464     slideIn : function(cb){
39465         if(!this.isSlid || this.el.hasActiveFx()){
39466             Roo.callback(cb);
39467             return;
39468         }
39469         this.isSlid = false;
39470         this.beforeSlide();
39471         this.el.slideOut(this.getSlideAnchor(), {
39472             callback: function(){
39473                 this.el.setLeftTop(-10000, -10000);
39474                 this.afterSlide();
39475                 this.afterSlideIn();
39476                 Roo.callback(cb);
39477             },
39478             scope: this,
39479             block: true
39480         });
39481     },
39482     
39483     slideInIf : function(e){
39484         if(!e.within(this.el)){
39485             this.slideIn();
39486         }
39487     },
39488
39489     animateCollapse : function(){
39490         this.beforeSlide();
39491         this.el.setStyle("z-index", 20000);
39492         var anchor = this.getSlideAnchor();
39493         this.el.slideOut(anchor, {
39494             callback : function(){
39495                 this.el.setStyle("z-index", "");
39496                 this.collapsedEl.slideIn(anchor, {duration:.3});
39497                 this.afterSlide();
39498                 this.el.setLocation(-10000,-10000);
39499                 this.el.hide();
39500                 this.fireEvent("collapsed", this);
39501             },
39502             scope: this,
39503             block: true
39504         });
39505     },
39506
39507     animateExpand : function(){
39508         this.beforeSlide();
39509         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39510         this.el.setStyle("z-index", 20000);
39511         this.collapsedEl.hide({
39512             duration:.1
39513         });
39514         this.el.slideIn(this.getSlideAnchor(), {
39515             callback : function(){
39516                 this.el.setStyle("z-index", "");
39517                 this.afterSlide();
39518                 if(this.split){
39519                     this.split.el.show();
39520                 }
39521                 this.fireEvent("invalidated", this);
39522                 this.fireEvent("expanded", this);
39523             },
39524             scope: this,
39525             block: true
39526         });
39527     },
39528
39529     anchors : {
39530         "west" : "left",
39531         "east" : "right",
39532         "north" : "top",
39533         "south" : "bottom"
39534     },
39535
39536     sanchors : {
39537         "west" : "l",
39538         "east" : "r",
39539         "north" : "t",
39540         "south" : "b"
39541     },
39542
39543     canchors : {
39544         "west" : "tl-tr",
39545         "east" : "tr-tl",
39546         "north" : "tl-bl",
39547         "south" : "bl-tl"
39548     },
39549
39550     getAnchor : function(){
39551         return this.anchors[this.position];
39552     },
39553
39554     getCollapseAnchor : function(){
39555         return this.canchors[this.position];
39556     },
39557
39558     getSlideAnchor : function(){
39559         return this.sanchors[this.position];
39560     },
39561
39562     getAlignAdj : function(){
39563         var cm = this.cmargins;
39564         switch(this.position){
39565             case "west":
39566                 return [0, 0];
39567             break;
39568             case "east":
39569                 return [0, 0];
39570             break;
39571             case "north":
39572                 return [0, 0];
39573             break;
39574             case "south":
39575                 return [0, 0];
39576             break;
39577         }
39578     },
39579
39580     getExpandAdj : function(){
39581         var c = this.collapsedEl, cm = this.cmargins;
39582         switch(this.position){
39583             case "west":
39584                 return [-(cm.right+c.getWidth()+cm.left), 0];
39585             break;
39586             case "east":
39587                 return [cm.right+c.getWidth()+cm.left, 0];
39588             break;
39589             case "north":
39590                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39591             break;
39592             case "south":
39593                 return [0, cm.top+cm.bottom+c.getHeight()];
39594             break;
39595         }
39596     }
39597 });/*
39598  * Based on:
39599  * Ext JS Library 1.1.1
39600  * Copyright(c) 2006-2007, Ext JS, LLC.
39601  *
39602  * Originally Released Under LGPL - original licence link has changed is not relivant.
39603  *
39604  * Fork - LGPL
39605  * <script type="text/javascript">
39606  */
39607 /*
39608  * These classes are private internal classes
39609  */
39610 Roo.bootstrap.layout.Center = function(config){
39611     config.region = "center";
39612     Roo.bootstrap.layout.Region.call(this, config);
39613     this.visible = true;
39614     this.minWidth = config.minWidth || 20;
39615     this.minHeight = config.minHeight || 20;
39616 };
39617
39618 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39619     hide : function(){
39620         // center panel can't be hidden
39621     },
39622     
39623     show : function(){
39624         // center panel can't be hidden
39625     },
39626     
39627     getMinWidth: function(){
39628         return this.minWidth;
39629     },
39630     
39631     getMinHeight: function(){
39632         return this.minHeight;
39633     }
39634 });
39635
39636
39637
39638
39639  
39640
39641
39642
39643
39644
39645
39646 Roo.bootstrap.layout.North = function(config)
39647 {
39648     config.region = 'north';
39649     config.cursor = 'n-resize';
39650     
39651     Roo.bootstrap.layout.Split.call(this, config);
39652     
39653     
39654     if(this.split){
39655         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39656         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39657         this.split.el.addClass("roo-layout-split-v");
39658     }
39659     //var size = config.initialSize || config.height;
39660     //if(this.el && typeof size != "undefined"){
39661     //    this.el.setHeight(size);
39662     //}
39663 };
39664 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39665 {
39666     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39667      
39668      
39669     onRender : function(ctr, pos)
39670     {
39671         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39672         var size = this.config.initialSize || this.config.height;
39673         if(this.el && typeof size != "undefined"){
39674             this.el.setHeight(size);
39675         }
39676     
39677     },
39678     
39679     getBox : function(){
39680         if(this.collapsed){
39681             return this.collapsedEl.getBox();
39682         }
39683         var box = this.el.getBox();
39684         if(this.split){
39685             box.height += this.split.el.getHeight();
39686         }
39687         return box;
39688     },
39689     
39690     updateBox : function(box){
39691         if(this.split && !this.collapsed){
39692             box.height -= this.split.el.getHeight();
39693             this.split.el.setLeft(box.x);
39694             this.split.el.setTop(box.y+box.height);
39695             this.split.el.setWidth(box.width);
39696         }
39697         if(this.collapsed){
39698             this.updateBody(box.width, null);
39699         }
39700         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39701     }
39702 });
39703
39704
39705
39706
39707
39708 Roo.bootstrap.layout.South = function(config){
39709     config.region = 'south';
39710     config.cursor = 's-resize';
39711     Roo.bootstrap.layout.Split.call(this, config);
39712     if(this.split){
39713         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39714         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39715         this.split.el.addClass("roo-layout-split-v");
39716     }
39717     
39718 };
39719
39720 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39721     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39722     
39723     onRender : function(ctr, pos)
39724     {
39725         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39726         var size = this.config.initialSize || this.config.height;
39727         if(this.el && typeof size != "undefined"){
39728             this.el.setHeight(size);
39729         }
39730     
39731     },
39732     
39733     getBox : function(){
39734         if(this.collapsed){
39735             return this.collapsedEl.getBox();
39736         }
39737         var box = this.el.getBox();
39738         if(this.split){
39739             var sh = this.split.el.getHeight();
39740             box.height += sh;
39741             box.y -= sh;
39742         }
39743         return box;
39744     },
39745     
39746     updateBox : function(box){
39747         if(this.split && !this.collapsed){
39748             var sh = this.split.el.getHeight();
39749             box.height -= sh;
39750             box.y += sh;
39751             this.split.el.setLeft(box.x);
39752             this.split.el.setTop(box.y-sh);
39753             this.split.el.setWidth(box.width);
39754         }
39755         if(this.collapsed){
39756             this.updateBody(box.width, null);
39757         }
39758         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39759     }
39760 });
39761
39762 Roo.bootstrap.layout.East = function(config){
39763     config.region = "east";
39764     config.cursor = "e-resize";
39765     Roo.bootstrap.layout.Split.call(this, config);
39766     if(this.split){
39767         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39768         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39769         this.split.el.addClass("roo-layout-split-h");
39770     }
39771     
39772 };
39773 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39774     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39775     
39776     onRender : function(ctr, pos)
39777     {
39778         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39779         var size = this.config.initialSize || this.config.width;
39780         if(this.el && typeof size != "undefined"){
39781             this.el.setWidth(size);
39782         }
39783     
39784     },
39785     
39786     getBox : function(){
39787         if(this.collapsed){
39788             return this.collapsedEl.getBox();
39789         }
39790         var box = this.el.getBox();
39791         if(this.split){
39792             var sw = this.split.el.getWidth();
39793             box.width += sw;
39794             box.x -= sw;
39795         }
39796         return box;
39797     },
39798
39799     updateBox : function(box){
39800         if(this.split && !this.collapsed){
39801             var sw = this.split.el.getWidth();
39802             box.width -= sw;
39803             this.split.el.setLeft(box.x);
39804             this.split.el.setTop(box.y);
39805             this.split.el.setHeight(box.height);
39806             box.x += sw;
39807         }
39808         if(this.collapsed){
39809             this.updateBody(null, box.height);
39810         }
39811         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39812     }
39813 });
39814
39815 Roo.bootstrap.layout.West = function(config){
39816     config.region = "west";
39817     config.cursor = "w-resize";
39818     
39819     Roo.bootstrap.layout.Split.call(this, config);
39820     if(this.split){
39821         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39822         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39823         this.split.el.addClass("roo-layout-split-h");
39824     }
39825     
39826 };
39827 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39828     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39829     
39830     onRender: function(ctr, pos)
39831     {
39832         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39833         var size = this.config.initialSize || this.config.width;
39834         if(typeof size != "undefined"){
39835             this.el.setWidth(size);
39836         }
39837     },
39838     
39839     getBox : function(){
39840         if(this.collapsed){
39841             return this.collapsedEl.getBox();
39842         }
39843         var box = this.el.getBox();
39844         if (box.width == 0) {
39845             box.width = this.config.width; // kludge?
39846         }
39847         if(this.split){
39848             box.width += this.split.el.getWidth();
39849         }
39850         return box;
39851     },
39852     
39853     updateBox : function(box){
39854         if(this.split && !this.collapsed){
39855             var sw = this.split.el.getWidth();
39856             box.width -= sw;
39857             this.split.el.setLeft(box.x+box.width);
39858             this.split.el.setTop(box.y);
39859             this.split.el.setHeight(box.height);
39860         }
39861         if(this.collapsed){
39862             this.updateBody(null, box.height);
39863         }
39864         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39865     }
39866 });Roo.namespace("Roo.bootstrap.panel");/*
39867  * Based on:
39868  * Ext JS Library 1.1.1
39869  * Copyright(c) 2006-2007, Ext JS, LLC.
39870  *
39871  * Originally Released Under LGPL - original licence link has changed is not relivant.
39872  *
39873  * Fork - LGPL
39874  * <script type="text/javascript">
39875  */
39876 /**
39877  * @class Roo.ContentPanel
39878  * @extends Roo.util.Observable
39879  * A basic ContentPanel element.
39880  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39881  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39882  * @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
39883  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39884  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39885  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39886  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39887  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39888  * @cfg {String} title          The title for this panel
39889  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39890  * @cfg {String} url            Calls {@link #setUrl} with this value
39891  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39892  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39893  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39894  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39895  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39896  * @cfg {Boolean} badges render the badges
39897  * @cfg {String} cls  extra classes to use  
39898  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39899
39900  * @constructor
39901  * Create a new ContentPanel.
39902  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39903  * @param {String/Object} config A string to set only the title or a config object
39904  * @param {String} content (optional) Set the HTML content for this panel
39905  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39906  */
39907 Roo.bootstrap.panel.Content = function( config){
39908     
39909     this.tpl = config.tpl || false;
39910     
39911     var el = config.el;
39912     var content = config.content;
39913
39914     if(config.autoCreate){ // xtype is available if this is called from factory
39915         el = Roo.id();
39916     }
39917     this.el = Roo.get(el);
39918     if(!this.el && config && config.autoCreate){
39919         if(typeof config.autoCreate == "object"){
39920             if(!config.autoCreate.id){
39921                 config.autoCreate.id = config.id||el;
39922             }
39923             this.el = Roo.DomHelper.append(document.body,
39924                         config.autoCreate, true);
39925         }else{
39926             var elcfg =  {
39927                 tag: "div",
39928                 cls: (config.cls || '') +
39929                     (config.background ? ' bg-' + config.background : '') +
39930                     " roo-layout-inactive-content",
39931                 id: config.id||el
39932             };
39933             if (config.iframe) {
39934                 elcfg.cn = [
39935                     {
39936                         tag : 'iframe',
39937                         style : 'border: 0px',
39938                         src : 'about:blank'
39939                     }
39940                 ];
39941             }
39942               
39943             if (config.html) {
39944                 elcfg.html = config.html;
39945                 
39946             }
39947                         
39948             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39949             if (config.iframe) {
39950                 this.iframeEl = this.el.select('iframe',true).first();
39951             }
39952             
39953         }
39954     } 
39955     this.closable = false;
39956     this.loaded = false;
39957     this.active = false;
39958    
39959       
39960     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39961         
39962         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39963         
39964         this.wrapEl = this.el; //this.el.wrap();
39965         var ti = [];
39966         if (config.toolbar.items) {
39967             ti = config.toolbar.items ;
39968             delete config.toolbar.items ;
39969         }
39970         
39971         var nitems = [];
39972         this.toolbar.render(this.wrapEl, 'before');
39973         for(var i =0;i < ti.length;i++) {
39974           //  Roo.log(['add child', items[i]]);
39975             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39976         }
39977         this.toolbar.items = nitems;
39978         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39979         delete config.toolbar;
39980         
39981     }
39982     /*
39983     // xtype created footer. - not sure if will work as we normally have to render first..
39984     if (this.footer && !this.footer.el && this.footer.xtype) {
39985         if (!this.wrapEl) {
39986             this.wrapEl = this.el.wrap();
39987         }
39988     
39989         this.footer.container = this.wrapEl.createChild();
39990          
39991         this.footer = Roo.factory(this.footer, Roo);
39992         
39993     }
39994     */
39995     
39996      if(typeof config == "string"){
39997         this.title = config;
39998     }else{
39999         Roo.apply(this, config);
40000     }
40001     
40002     if(this.resizeEl){
40003         this.resizeEl = Roo.get(this.resizeEl, true);
40004     }else{
40005         this.resizeEl = this.el;
40006     }
40007     // handle view.xtype
40008     
40009  
40010     
40011     
40012     this.addEvents({
40013         /**
40014          * @event activate
40015          * Fires when this panel is activated. 
40016          * @param {Roo.ContentPanel} this
40017          */
40018         "activate" : true,
40019         /**
40020          * @event deactivate
40021          * Fires when this panel is activated. 
40022          * @param {Roo.ContentPanel} this
40023          */
40024         "deactivate" : true,
40025
40026         /**
40027          * @event resize
40028          * Fires when this panel is resized if fitToFrame is true.
40029          * @param {Roo.ContentPanel} this
40030          * @param {Number} width The width after any component adjustments
40031          * @param {Number} height The height after any component adjustments
40032          */
40033         "resize" : true,
40034         
40035          /**
40036          * @event render
40037          * Fires when this tab is created
40038          * @param {Roo.ContentPanel} this
40039          */
40040         "render" : true
40041         
40042         
40043         
40044     });
40045     
40046
40047     
40048     
40049     if(this.autoScroll && !this.iframe){
40050         this.resizeEl.setStyle("overflow", "auto");
40051     } else {
40052         // fix randome scrolling
40053         //this.el.on('scroll', function() {
40054         //    Roo.log('fix random scolling');
40055         //    this.scrollTo('top',0); 
40056         //});
40057     }
40058     content = content || this.content;
40059     if(content){
40060         this.setContent(content);
40061     }
40062     if(config && config.url){
40063         this.setUrl(this.url, this.params, this.loadOnce);
40064     }
40065     
40066     
40067     
40068     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40069     
40070     if (this.view && typeof(this.view.xtype) != 'undefined') {
40071         this.view.el = this.el.appendChild(document.createElement("div"));
40072         this.view = Roo.factory(this.view); 
40073         this.view.render  &&  this.view.render(false, '');  
40074     }
40075     
40076     
40077     this.fireEvent('render', this);
40078 };
40079
40080 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40081     
40082     cls : '',
40083     background : '',
40084     
40085     tabTip : '',
40086     
40087     iframe : false,
40088     iframeEl : false,
40089     
40090     setRegion : function(region){
40091         this.region = region;
40092         this.setActiveClass(region && !this.background);
40093     },
40094     
40095     
40096     setActiveClass: function(state)
40097     {
40098         if(state){
40099            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40100            this.el.setStyle('position','relative');
40101         }else{
40102            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40103            this.el.setStyle('position', 'absolute');
40104         } 
40105     },
40106     
40107     /**
40108      * Returns the toolbar for this Panel if one was configured. 
40109      * @return {Roo.Toolbar} 
40110      */
40111     getToolbar : function(){
40112         return this.toolbar;
40113     },
40114     
40115     setActiveState : function(active)
40116     {
40117         this.active = active;
40118         this.setActiveClass(active);
40119         if(!active){
40120             if(this.fireEvent("deactivate", this) === false){
40121                 return false;
40122             }
40123             return true;
40124         }
40125         this.fireEvent("activate", this);
40126         return true;
40127     },
40128     /**
40129      * Updates this panel's element (not for iframe)
40130      * @param {String} content The new content
40131      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40132     */
40133     setContent : function(content, loadScripts){
40134         if (this.iframe) {
40135             return;
40136         }
40137         
40138         this.el.update(content, loadScripts);
40139     },
40140
40141     ignoreResize : function(w, h){
40142         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40143             return true;
40144         }else{
40145             this.lastSize = {width: w, height: h};
40146             return false;
40147         }
40148     },
40149     /**
40150      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40151      * @return {Roo.UpdateManager} The UpdateManager
40152      */
40153     getUpdateManager : function(){
40154         if (this.iframe) {
40155             return false;
40156         }
40157         return this.el.getUpdateManager();
40158     },
40159      /**
40160      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40161      * Does not work with IFRAME contents
40162      * @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:
40163 <pre><code>
40164 panel.load({
40165     url: "your-url.php",
40166     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40167     callback: yourFunction,
40168     scope: yourObject, //(optional scope)
40169     discardUrl: false,
40170     nocache: false,
40171     text: "Loading...",
40172     timeout: 30,
40173     scripts: false
40174 });
40175 </code></pre>
40176      
40177      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40178      * 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.
40179      * @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}
40180      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40181      * @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.
40182      * @return {Roo.ContentPanel} this
40183      */
40184     load : function(){
40185         
40186         if (this.iframe) {
40187             return this;
40188         }
40189         
40190         var um = this.el.getUpdateManager();
40191         um.update.apply(um, arguments);
40192         return this;
40193     },
40194
40195
40196     /**
40197      * 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.
40198      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40199      * @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)
40200      * @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)
40201      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40202      */
40203     setUrl : function(url, params, loadOnce){
40204         if (this.iframe) {
40205             this.iframeEl.dom.src = url;
40206             return false;
40207         }
40208         
40209         if(this.refreshDelegate){
40210             this.removeListener("activate", this.refreshDelegate);
40211         }
40212         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40213         this.on("activate", this.refreshDelegate);
40214         return this.el.getUpdateManager();
40215     },
40216     
40217     _handleRefresh : function(url, params, loadOnce){
40218         if(!loadOnce || !this.loaded){
40219             var updater = this.el.getUpdateManager();
40220             updater.update(url, params, this._setLoaded.createDelegate(this));
40221         }
40222     },
40223     
40224     _setLoaded : function(){
40225         this.loaded = true;
40226     }, 
40227     
40228     /**
40229      * Returns this panel's id
40230      * @return {String} 
40231      */
40232     getId : function(){
40233         return this.el.id;
40234     },
40235     
40236     /** 
40237      * Returns this panel's element - used by regiosn to add.
40238      * @return {Roo.Element} 
40239      */
40240     getEl : function(){
40241         return this.wrapEl || this.el;
40242     },
40243     
40244    
40245     
40246     adjustForComponents : function(width, height)
40247     {
40248         //Roo.log('adjustForComponents ');
40249         if(this.resizeEl != this.el){
40250             width -= this.el.getFrameWidth('lr');
40251             height -= this.el.getFrameWidth('tb');
40252         }
40253         if(this.toolbar){
40254             var te = this.toolbar.getEl();
40255             te.setWidth(width);
40256             height -= te.getHeight();
40257         }
40258         if(this.footer){
40259             var te = this.footer.getEl();
40260             te.setWidth(width);
40261             height -= te.getHeight();
40262         }
40263         
40264         
40265         if(this.adjustments){
40266             width += this.adjustments[0];
40267             height += this.adjustments[1];
40268         }
40269         return {"width": width, "height": height};
40270     },
40271     
40272     setSize : function(width, height){
40273         if(this.fitToFrame && !this.ignoreResize(width, height)){
40274             if(this.fitContainer && this.resizeEl != this.el){
40275                 this.el.setSize(width, height);
40276             }
40277             var size = this.adjustForComponents(width, height);
40278             if (this.iframe) {
40279                 this.iframeEl.setSize(width,height);
40280             }
40281             
40282             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40283             this.fireEvent('resize', this, size.width, size.height);
40284             
40285             
40286         }
40287     },
40288     
40289     /**
40290      * Returns this panel's title
40291      * @return {String} 
40292      */
40293     getTitle : function(){
40294         
40295         if (typeof(this.title) != 'object') {
40296             return this.title;
40297         }
40298         
40299         var t = '';
40300         for (var k in this.title) {
40301             if (!this.title.hasOwnProperty(k)) {
40302                 continue;
40303             }
40304             
40305             if (k.indexOf('-') >= 0) {
40306                 var s = k.split('-');
40307                 for (var i = 0; i<s.length; i++) {
40308                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40309                 }
40310             } else {
40311                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40312             }
40313         }
40314         return t;
40315     },
40316     
40317     /**
40318      * Set this panel's title
40319      * @param {String} title
40320      */
40321     setTitle : function(title){
40322         this.title = title;
40323         if(this.region){
40324             this.region.updatePanelTitle(this, title);
40325         }
40326     },
40327     
40328     /**
40329      * Returns true is this panel was configured to be closable
40330      * @return {Boolean} 
40331      */
40332     isClosable : function(){
40333         return this.closable;
40334     },
40335     
40336     beforeSlide : function(){
40337         this.el.clip();
40338         this.resizeEl.clip();
40339     },
40340     
40341     afterSlide : function(){
40342         this.el.unclip();
40343         this.resizeEl.unclip();
40344     },
40345     
40346     /**
40347      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40348      *   Will fail silently if the {@link #setUrl} method has not been called.
40349      *   This does not activate the panel, just updates its content.
40350      */
40351     refresh : function(){
40352         if(this.refreshDelegate){
40353            this.loaded = false;
40354            this.refreshDelegate();
40355         }
40356     },
40357     
40358     /**
40359      * Destroys this panel
40360      */
40361     destroy : function(){
40362         this.el.removeAllListeners();
40363         var tempEl = document.createElement("span");
40364         tempEl.appendChild(this.el.dom);
40365         tempEl.innerHTML = "";
40366         this.el.remove();
40367         this.el = null;
40368     },
40369     
40370     /**
40371      * form - if the content panel contains a form - this is a reference to it.
40372      * @type {Roo.form.Form}
40373      */
40374     form : false,
40375     /**
40376      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40377      *    This contains a reference to it.
40378      * @type {Roo.View}
40379      */
40380     view : false,
40381     
40382       /**
40383      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40384      * <pre><code>
40385
40386 layout.addxtype({
40387        xtype : 'Form',
40388        items: [ .... ]
40389    }
40390 );
40391
40392 </code></pre>
40393      * @param {Object} cfg Xtype definition of item to add.
40394      */
40395     
40396     
40397     getChildContainer: function () {
40398         return this.getEl();
40399     }
40400     
40401     
40402     /*
40403         var  ret = new Roo.factory(cfg);
40404         return ret;
40405         
40406         
40407         // add form..
40408         if (cfg.xtype.match(/^Form$/)) {
40409             
40410             var el;
40411             //if (this.footer) {
40412             //    el = this.footer.container.insertSibling(false, 'before');
40413             //} else {
40414                 el = this.el.createChild();
40415             //}
40416
40417             this.form = new  Roo.form.Form(cfg);
40418             
40419             
40420             if ( this.form.allItems.length) {
40421                 this.form.render(el.dom);
40422             }
40423             return this.form;
40424         }
40425         // should only have one of theses..
40426         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40427             // views.. should not be just added - used named prop 'view''
40428             
40429             cfg.el = this.el.appendChild(document.createElement("div"));
40430             // factory?
40431             
40432             var ret = new Roo.factory(cfg);
40433              
40434              ret.render && ret.render(false, ''); // render blank..
40435             this.view = ret;
40436             return ret;
40437         }
40438         return false;
40439     }
40440     \*/
40441 });
40442  
40443 /**
40444  * @class Roo.bootstrap.panel.Grid
40445  * @extends Roo.bootstrap.panel.Content
40446  * @constructor
40447  * Create a new GridPanel.
40448  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40449  * @param {Object} config A the config object
40450   
40451  */
40452
40453
40454
40455 Roo.bootstrap.panel.Grid = function(config)
40456 {
40457     
40458       
40459     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40460         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40461
40462     config.el = this.wrapper;
40463     //this.el = this.wrapper;
40464     
40465       if (config.container) {
40466         // ctor'ed from a Border/panel.grid
40467         
40468         
40469         this.wrapper.setStyle("overflow", "hidden");
40470         this.wrapper.addClass('roo-grid-container');
40471
40472     }
40473     
40474     
40475     if(config.toolbar){
40476         var tool_el = this.wrapper.createChild();    
40477         this.toolbar = Roo.factory(config.toolbar);
40478         var ti = [];
40479         if (config.toolbar.items) {
40480             ti = config.toolbar.items ;
40481             delete config.toolbar.items ;
40482         }
40483         
40484         var nitems = [];
40485         this.toolbar.render(tool_el);
40486         for(var i =0;i < ti.length;i++) {
40487           //  Roo.log(['add child', items[i]]);
40488             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40489         }
40490         this.toolbar.items = nitems;
40491         
40492         delete config.toolbar;
40493     }
40494     
40495     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40496     config.grid.scrollBody = true;;
40497     config.grid.monitorWindowResize = false; // turn off autosizing
40498     config.grid.autoHeight = false;
40499     config.grid.autoWidth = false;
40500     
40501     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40502     
40503     if (config.background) {
40504         // render grid on panel activation (if panel background)
40505         this.on('activate', function(gp) {
40506             if (!gp.grid.rendered) {
40507                 gp.grid.render(this.wrapper);
40508                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40509             }
40510         });
40511             
40512     } else {
40513         this.grid.render(this.wrapper);
40514         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40515
40516     }
40517     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40518     // ??? needed ??? config.el = this.wrapper;
40519     
40520     
40521     
40522   
40523     // xtype created footer. - not sure if will work as we normally have to render first..
40524     if (this.footer && !this.footer.el && this.footer.xtype) {
40525         
40526         var ctr = this.grid.getView().getFooterPanel(true);
40527         this.footer.dataSource = this.grid.dataSource;
40528         this.footer = Roo.factory(this.footer, Roo);
40529         this.footer.render(ctr);
40530         
40531     }
40532     
40533     
40534     
40535     
40536      
40537 };
40538
40539 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40540     getId : function(){
40541         return this.grid.id;
40542     },
40543     
40544     /**
40545      * Returns the grid for this panel
40546      * @return {Roo.bootstrap.Table} 
40547      */
40548     getGrid : function(){
40549         return this.grid;    
40550     },
40551     
40552     setSize : function(width, height){
40553         if(!this.ignoreResize(width, height)){
40554             var grid = this.grid;
40555             var size = this.adjustForComponents(width, height);
40556             // tfoot is not a footer?
40557           
40558             
40559             var gridel = grid.getGridEl();
40560             gridel.setSize(size.width, size.height);
40561             
40562             var tbd = grid.getGridEl().select('tbody', true).first();
40563             var thd = grid.getGridEl().select('thead',true).first();
40564             var tbf= grid.getGridEl().select('tfoot', true).first();
40565
40566             if (tbf) {
40567                 size.height -= tbf.getHeight();
40568             }
40569             if (thd) {
40570                 size.height -= thd.getHeight();
40571             }
40572             
40573             tbd.setSize(size.width, size.height );
40574             // this is for the account management tab -seems to work there.
40575             var thd = grid.getGridEl().select('thead',true).first();
40576             //if (tbd) {
40577             //    tbd.setSize(size.width, size.height - thd.getHeight());
40578             //}
40579              
40580             grid.autoSize();
40581         }
40582     },
40583      
40584     
40585     
40586     beforeSlide : function(){
40587         this.grid.getView().scroller.clip();
40588     },
40589     
40590     afterSlide : function(){
40591         this.grid.getView().scroller.unclip();
40592     },
40593     
40594     destroy : function(){
40595         this.grid.destroy();
40596         delete this.grid;
40597         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40598     }
40599 });
40600
40601 /**
40602  * @class Roo.bootstrap.panel.Nest
40603  * @extends Roo.bootstrap.panel.Content
40604  * @constructor
40605  * Create a new Panel, that can contain a layout.Border.
40606  * 
40607  * 
40608  * @param {Roo.BorderLayout} layout The layout for this panel
40609  * @param {String/Object} config A string to set only the title or a config object
40610  */
40611 Roo.bootstrap.panel.Nest = function(config)
40612 {
40613     // construct with only one argument..
40614     /* FIXME - implement nicer consturctors
40615     if (layout.layout) {
40616         config = layout;
40617         layout = config.layout;
40618         delete config.layout;
40619     }
40620     if (layout.xtype && !layout.getEl) {
40621         // then layout needs constructing..
40622         layout = Roo.factory(layout, Roo);
40623     }
40624     */
40625     
40626     config.el =  config.layout.getEl();
40627     
40628     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40629     
40630     config.layout.monitorWindowResize = false; // turn off autosizing
40631     this.layout = config.layout;
40632     this.layout.getEl().addClass("roo-layout-nested-layout");
40633     this.layout.parent = this;
40634     
40635     
40636     
40637     
40638 };
40639
40640 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40641
40642     setSize : function(width, height){
40643         if(!this.ignoreResize(width, height)){
40644             var size = this.adjustForComponents(width, height);
40645             var el = this.layout.getEl();
40646             if (size.height < 1) {
40647                 el.setWidth(size.width);   
40648             } else {
40649                 el.setSize(size.width, size.height);
40650             }
40651             var touch = el.dom.offsetWidth;
40652             this.layout.layout();
40653             // ie requires a double layout on the first pass
40654             if(Roo.isIE && !this.initialized){
40655                 this.initialized = true;
40656                 this.layout.layout();
40657             }
40658         }
40659     },
40660     
40661     // activate all subpanels if not currently active..
40662     
40663     setActiveState : function(active){
40664         this.active = active;
40665         this.setActiveClass(active);
40666         
40667         if(!active){
40668             this.fireEvent("deactivate", this);
40669             return;
40670         }
40671         
40672         this.fireEvent("activate", this);
40673         // not sure if this should happen before or after..
40674         if (!this.layout) {
40675             return; // should not happen..
40676         }
40677         var reg = false;
40678         for (var r in this.layout.regions) {
40679             reg = this.layout.getRegion(r);
40680             if (reg.getActivePanel()) {
40681                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40682                 reg.setActivePanel(reg.getActivePanel());
40683                 continue;
40684             }
40685             if (!reg.panels.length) {
40686                 continue;
40687             }
40688             reg.showPanel(reg.getPanel(0));
40689         }
40690         
40691         
40692         
40693         
40694     },
40695     
40696     /**
40697      * Returns the nested BorderLayout for this panel
40698      * @return {Roo.BorderLayout} 
40699      */
40700     getLayout : function(){
40701         return this.layout;
40702     },
40703     
40704      /**
40705      * Adds a xtype elements to the layout of the nested panel
40706      * <pre><code>
40707
40708 panel.addxtype({
40709        xtype : 'ContentPanel',
40710        region: 'west',
40711        items: [ .... ]
40712    }
40713 );
40714
40715 panel.addxtype({
40716         xtype : 'NestedLayoutPanel',
40717         region: 'west',
40718         layout: {
40719            center: { },
40720            west: { }   
40721         },
40722         items : [ ... list of content panels or nested layout panels.. ]
40723    }
40724 );
40725 </code></pre>
40726      * @param {Object} cfg Xtype definition of item to add.
40727      */
40728     addxtype : function(cfg) {
40729         return this.layout.addxtype(cfg);
40730     
40731     }
40732 });/*
40733  * Based on:
40734  * Ext JS Library 1.1.1
40735  * Copyright(c) 2006-2007, Ext JS, LLC.
40736  *
40737  * Originally Released Under LGPL - original licence link has changed is not relivant.
40738  *
40739  * Fork - LGPL
40740  * <script type="text/javascript">
40741  */
40742 /**
40743  * @class Roo.TabPanel
40744  * @extends Roo.util.Observable
40745  * A lightweight tab container.
40746  * <br><br>
40747  * Usage:
40748  * <pre><code>
40749 // basic tabs 1, built from existing content
40750 var tabs = new Roo.TabPanel("tabs1");
40751 tabs.addTab("script", "View Script");
40752 tabs.addTab("markup", "View Markup");
40753 tabs.activate("script");
40754
40755 // more advanced tabs, built from javascript
40756 var jtabs = new Roo.TabPanel("jtabs");
40757 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40758
40759 // set up the UpdateManager
40760 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40761 var updater = tab2.getUpdateManager();
40762 updater.setDefaultUrl("ajax1.htm");
40763 tab2.on('activate', updater.refresh, updater, true);
40764
40765 // Use setUrl for Ajax loading
40766 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40767 tab3.setUrl("ajax2.htm", null, true);
40768
40769 // Disabled tab
40770 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40771 tab4.disable();
40772
40773 jtabs.activate("jtabs-1");
40774  * </code></pre>
40775  * @constructor
40776  * Create a new TabPanel.
40777  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40778  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40779  */
40780 Roo.bootstrap.panel.Tabs = function(config){
40781     /**
40782     * The container element for this TabPanel.
40783     * @type Roo.Element
40784     */
40785     this.el = Roo.get(config.el);
40786     delete config.el;
40787     if(config){
40788         if(typeof config == "boolean"){
40789             this.tabPosition = config ? "bottom" : "top";
40790         }else{
40791             Roo.apply(this, config);
40792         }
40793     }
40794     
40795     if(this.tabPosition == "bottom"){
40796         // if tabs are at the bottom = create the body first.
40797         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40798         this.el.addClass("roo-tabs-bottom");
40799     }
40800     // next create the tabs holders
40801     
40802     if (this.tabPosition == "west"){
40803         
40804         var reg = this.region; // fake it..
40805         while (reg) {
40806             if (!reg.mgr.parent) {
40807                 break;
40808             }
40809             reg = reg.mgr.parent.region;
40810         }
40811         Roo.log("got nest?");
40812         Roo.log(reg);
40813         if (reg.mgr.getRegion('west')) {
40814             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40815             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40816             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40817             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40818             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40819         
40820             
40821         }
40822         
40823         
40824     } else {
40825      
40826         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40827         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40828         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40829         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40830     }
40831     
40832     
40833     if(Roo.isIE){
40834         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40835     }
40836     
40837     // finally - if tabs are at the top, then create the body last..
40838     if(this.tabPosition != "bottom"){
40839         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40840          * @type Roo.Element
40841          */
40842         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40843         this.el.addClass("roo-tabs-top");
40844     }
40845     this.items = [];
40846
40847     this.bodyEl.setStyle("position", "relative");
40848
40849     this.active = null;
40850     this.activateDelegate = this.activate.createDelegate(this);
40851
40852     this.addEvents({
40853         /**
40854          * @event tabchange
40855          * Fires when the active tab changes
40856          * @param {Roo.TabPanel} this
40857          * @param {Roo.TabPanelItem} activePanel The new active tab
40858          */
40859         "tabchange": true,
40860         /**
40861          * @event beforetabchange
40862          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40863          * @param {Roo.TabPanel} this
40864          * @param {Object} e Set cancel to true on this object to cancel the tab change
40865          * @param {Roo.TabPanelItem} tab The tab being changed to
40866          */
40867         "beforetabchange" : true
40868     });
40869
40870     Roo.EventManager.onWindowResize(this.onResize, this);
40871     this.cpad = this.el.getPadding("lr");
40872     this.hiddenCount = 0;
40873
40874
40875     // toolbar on the tabbar support...
40876     if (this.toolbar) {
40877         alert("no toolbar support yet");
40878         this.toolbar  = false;
40879         /*
40880         var tcfg = this.toolbar;
40881         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40882         this.toolbar = new Roo.Toolbar(tcfg);
40883         if (Roo.isSafari) {
40884             var tbl = tcfg.container.child('table', true);
40885             tbl.setAttribute('width', '100%');
40886         }
40887         */
40888         
40889     }
40890    
40891
40892
40893     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40894 };
40895
40896 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40897     /*
40898      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40899      */
40900     tabPosition : "top",
40901     /*
40902      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40903      */
40904     currentTabWidth : 0,
40905     /*
40906      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40907      */
40908     minTabWidth : 40,
40909     /*
40910      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40911      */
40912     maxTabWidth : 250,
40913     /*
40914      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40915      */
40916     preferredTabWidth : 175,
40917     /*
40918      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40919      */
40920     resizeTabs : false,
40921     /*
40922      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40923      */
40924     monitorResize : true,
40925     /*
40926      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40927      */
40928     toolbar : false,  // set by caller..
40929     
40930     region : false, /// set by caller
40931     
40932     disableTooltips : true, // not used yet...
40933
40934     /**
40935      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40936      * @param {String} id The id of the div to use <b>or create</b>
40937      * @param {String} text The text for the tab
40938      * @param {String} content (optional) Content to put in the TabPanelItem body
40939      * @param {Boolean} closable (optional) True to create a close icon on the tab
40940      * @return {Roo.TabPanelItem} The created TabPanelItem
40941      */
40942     addTab : function(id, text, content, closable, tpl)
40943     {
40944         var item = new Roo.bootstrap.panel.TabItem({
40945             panel: this,
40946             id : id,
40947             text : text,
40948             closable : closable,
40949             tpl : tpl
40950         });
40951         this.addTabItem(item);
40952         if(content){
40953             item.setContent(content);
40954         }
40955         return item;
40956     },
40957
40958     /**
40959      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40960      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40961      * @return {Roo.TabPanelItem}
40962      */
40963     getTab : function(id){
40964         return this.items[id];
40965     },
40966
40967     /**
40968      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40969      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40970      */
40971     hideTab : function(id){
40972         var t = this.items[id];
40973         if(!t.isHidden()){
40974            t.setHidden(true);
40975            this.hiddenCount++;
40976            this.autoSizeTabs();
40977         }
40978     },
40979
40980     /**
40981      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40982      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40983      */
40984     unhideTab : function(id){
40985         var t = this.items[id];
40986         if(t.isHidden()){
40987            t.setHidden(false);
40988            this.hiddenCount--;
40989            this.autoSizeTabs();
40990         }
40991     },
40992
40993     /**
40994      * Adds an existing {@link Roo.TabPanelItem}.
40995      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40996      */
40997     addTabItem : function(item)
40998     {
40999         this.items[item.id] = item;
41000         this.items.push(item);
41001         this.autoSizeTabs();
41002       //  if(this.resizeTabs){
41003     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41004   //         this.autoSizeTabs();
41005 //        }else{
41006 //            item.autoSize();
41007        // }
41008     },
41009
41010     /**
41011      * Removes a {@link Roo.TabPanelItem}.
41012      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41013      */
41014     removeTab : function(id){
41015         var items = this.items;
41016         var tab = items[id];
41017         if(!tab) { return; }
41018         var index = items.indexOf(tab);
41019         if(this.active == tab && items.length > 1){
41020             var newTab = this.getNextAvailable(index);
41021             if(newTab) {
41022                 newTab.activate();
41023             }
41024         }
41025         this.stripEl.dom.removeChild(tab.pnode.dom);
41026         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41027             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41028         }
41029         items.splice(index, 1);
41030         delete this.items[tab.id];
41031         tab.fireEvent("close", tab);
41032         tab.purgeListeners();
41033         this.autoSizeTabs();
41034     },
41035
41036     getNextAvailable : function(start){
41037         var items = this.items;
41038         var index = start;
41039         // look for a next tab that will slide over to
41040         // replace the one being removed
41041         while(index < items.length){
41042             var item = items[++index];
41043             if(item && !item.isHidden()){
41044                 return item;
41045             }
41046         }
41047         // if one isn't found select the previous tab (on the left)
41048         index = start;
41049         while(index >= 0){
41050             var item = items[--index];
41051             if(item && !item.isHidden()){
41052                 return item;
41053             }
41054         }
41055         return null;
41056     },
41057
41058     /**
41059      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41060      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41061      */
41062     disableTab : function(id){
41063         var tab = this.items[id];
41064         if(tab && this.active != tab){
41065             tab.disable();
41066         }
41067     },
41068
41069     /**
41070      * Enables a {@link Roo.TabPanelItem} that is disabled.
41071      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41072      */
41073     enableTab : function(id){
41074         var tab = this.items[id];
41075         tab.enable();
41076     },
41077
41078     /**
41079      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41080      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41081      * @return {Roo.TabPanelItem} The TabPanelItem.
41082      */
41083     activate : function(id)
41084     {
41085         //Roo.log('activite:'  + id);
41086         
41087         var tab = this.items[id];
41088         if(!tab){
41089             return null;
41090         }
41091         if(tab == this.active || tab.disabled){
41092             return tab;
41093         }
41094         var e = {};
41095         this.fireEvent("beforetabchange", this, e, tab);
41096         if(e.cancel !== true && !tab.disabled){
41097             if(this.active){
41098                 this.active.hide();
41099             }
41100             this.active = this.items[id];
41101             this.active.show();
41102             this.fireEvent("tabchange", this, this.active);
41103         }
41104         return tab;
41105     },
41106
41107     /**
41108      * Gets the active {@link Roo.TabPanelItem}.
41109      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41110      */
41111     getActiveTab : function(){
41112         return this.active;
41113     },
41114
41115     /**
41116      * Updates the tab body element to fit the height of the container element
41117      * for overflow scrolling
41118      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41119      */
41120     syncHeight : function(targetHeight){
41121         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41122         var bm = this.bodyEl.getMargins();
41123         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41124         this.bodyEl.setHeight(newHeight);
41125         return newHeight;
41126     },
41127
41128     onResize : function(){
41129         if(this.monitorResize){
41130             this.autoSizeTabs();
41131         }
41132     },
41133
41134     /**
41135      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41136      */
41137     beginUpdate : function(){
41138         this.updating = true;
41139     },
41140
41141     /**
41142      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41143      */
41144     endUpdate : function(){
41145         this.updating = false;
41146         this.autoSizeTabs();
41147     },
41148
41149     /**
41150      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41151      */
41152     autoSizeTabs : function()
41153     {
41154         var count = this.items.length;
41155         var vcount = count - this.hiddenCount;
41156         
41157         if (vcount < 2) {
41158             this.stripEl.hide();
41159         } else {
41160             this.stripEl.show();
41161         }
41162         
41163         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41164             return;
41165         }
41166         
41167         
41168         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41169         var availWidth = Math.floor(w / vcount);
41170         var b = this.stripBody;
41171         if(b.getWidth() > w){
41172             var tabs = this.items;
41173             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41174             if(availWidth < this.minTabWidth){
41175                 /*if(!this.sleft){    // incomplete scrolling code
41176                     this.createScrollButtons();
41177                 }
41178                 this.showScroll();
41179                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41180             }
41181         }else{
41182             if(this.currentTabWidth < this.preferredTabWidth){
41183                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41184             }
41185         }
41186     },
41187
41188     /**
41189      * Returns the number of tabs in this TabPanel.
41190      * @return {Number}
41191      */
41192      getCount : function(){
41193          return this.items.length;
41194      },
41195
41196     /**
41197      * Resizes all the tabs to the passed width
41198      * @param {Number} The new width
41199      */
41200     setTabWidth : function(width){
41201         this.currentTabWidth = width;
41202         for(var i = 0, len = this.items.length; i < len; i++) {
41203                 if(!this.items[i].isHidden()) {
41204                 this.items[i].setWidth(width);
41205             }
41206         }
41207     },
41208
41209     /**
41210      * Destroys this TabPanel
41211      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41212      */
41213     destroy : function(removeEl){
41214         Roo.EventManager.removeResizeListener(this.onResize, this);
41215         for(var i = 0, len = this.items.length; i < len; i++){
41216             this.items[i].purgeListeners();
41217         }
41218         if(removeEl === true){
41219             this.el.update("");
41220             this.el.remove();
41221         }
41222     },
41223     
41224     createStrip : function(container)
41225     {
41226         var strip = document.createElement("nav");
41227         strip.className = Roo.bootstrap.version == 4 ?
41228             "navbar-light bg-light" : 
41229             "navbar navbar-default"; //"x-tabs-wrap";
41230         container.appendChild(strip);
41231         return strip;
41232     },
41233     
41234     createStripList : function(strip)
41235     {
41236         // div wrapper for retard IE
41237         // returns the "tr" element.
41238         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41239         //'<div class="x-tabs-strip-wrap">'+
41240           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41241           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41242         return strip.firstChild; //.firstChild.firstChild.firstChild;
41243     },
41244     createBody : function(container)
41245     {
41246         var body = document.createElement("div");
41247         Roo.id(body, "tab-body");
41248         //Roo.fly(body).addClass("x-tabs-body");
41249         Roo.fly(body).addClass("tab-content");
41250         container.appendChild(body);
41251         return body;
41252     },
41253     createItemBody :function(bodyEl, id){
41254         var body = Roo.getDom(id);
41255         if(!body){
41256             body = document.createElement("div");
41257             body.id = id;
41258         }
41259         //Roo.fly(body).addClass("x-tabs-item-body");
41260         Roo.fly(body).addClass("tab-pane");
41261          bodyEl.insertBefore(body, bodyEl.firstChild);
41262         return body;
41263     },
41264     /** @private */
41265     createStripElements :  function(stripEl, text, closable, tpl)
41266     {
41267         var td = document.createElement("li"); // was td..
41268         td.className = 'nav-item';
41269         
41270         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41271         
41272         
41273         stripEl.appendChild(td);
41274         /*if(closable){
41275             td.className = "x-tabs-closable";
41276             if(!this.closeTpl){
41277                 this.closeTpl = new Roo.Template(
41278                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41279                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41280                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41281                 );
41282             }
41283             var el = this.closeTpl.overwrite(td, {"text": text});
41284             var close = el.getElementsByTagName("div")[0];
41285             var inner = el.getElementsByTagName("em")[0];
41286             return {"el": el, "close": close, "inner": inner};
41287         } else {
41288         */
41289         // not sure what this is..
41290 //            if(!this.tabTpl){
41291                 //this.tabTpl = new Roo.Template(
41292                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41293                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41294                 //);
41295 //                this.tabTpl = new Roo.Template(
41296 //                   '<a href="#">' +
41297 //                   '<span unselectable="on"' +
41298 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41299 //                            ' >{text}</span></a>'
41300 //                );
41301 //                
41302 //            }
41303
41304
41305             var template = tpl || this.tabTpl || false;
41306             
41307             if(!template){
41308                 template =  new Roo.Template(
41309                         Roo.bootstrap.version == 4 ? 
41310                             (
41311                                 '<a class="nav-link" href="#" unselectable="on"' +
41312                                      (this.disableTooltips ? '' : ' title="{text}"') +
41313                                      ' >{text}</a>'
41314                             ) : (
41315                                 '<a class="nav-link" href="#">' +
41316                                 '<span unselectable="on"' +
41317                                          (this.disableTooltips ? '' : ' title="{text}"') +
41318                                     ' >{text}</span></a>'
41319                             )
41320                 );
41321             }
41322             
41323             switch (typeof(template)) {
41324                 case 'object' :
41325                     break;
41326                 case 'string' :
41327                     template = new Roo.Template(template);
41328                     break;
41329                 default :
41330                     break;
41331             }
41332             
41333             var el = template.overwrite(td, {"text": text});
41334             
41335             var inner = el.getElementsByTagName("span")[0];
41336             
41337             return {"el": el, "inner": inner};
41338             
41339     }
41340         
41341     
41342 });
41343
41344 /**
41345  * @class Roo.TabPanelItem
41346  * @extends Roo.util.Observable
41347  * Represents an individual item (tab plus body) in a TabPanel.
41348  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41349  * @param {String} id The id of this TabPanelItem
41350  * @param {String} text The text for the tab of this TabPanelItem
41351  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41352  */
41353 Roo.bootstrap.panel.TabItem = function(config){
41354     /**
41355      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41356      * @type Roo.TabPanel
41357      */
41358     this.tabPanel = config.panel;
41359     /**
41360      * The id for this TabPanelItem
41361      * @type String
41362      */
41363     this.id = config.id;
41364     /** @private */
41365     this.disabled = false;
41366     /** @private */
41367     this.text = config.text;
41368     /** @private */
41369     this.loaded = false;
41370     this.closable = config.closable;
41371
41372     /**
41373      * The body element for this TabPanelItem.
41374      * @type Roo.Element
41375      */
41376     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41377     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41378     this.bodyEl.setStyle("display", "block");
41379     this.bodyEl.setStyle("zoom", "1");
41380     //this.hideAction();
41381
41382     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41383     /** @private */
41384     this.el = Roo.get(els.el);
41385     this.inner = Roo.get(els.inner, true);
41386      this.textEl = Roo.bootstrap.version == 4 ?
41387         this.el : Roo.get(this.el.dom.firstChild, true);
41388
41389     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41390     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41391
41392     
41393 //    this.el.on("mousedown", this.onTabMouseDown, this);
41394     this.el.on("click", this.onTabClick, this);
41395     /** @private */
41396     if(config.closable){
41397         var c = Roo.get(els.close, true);
41398         c.dom.title = this.closeText;
41399         c.addClassOnOver("close-over");
41400         c.on("click", this.closeClick, this);
41401      }
41402
41403     this.addEvents({
41404          /**
41405          * @event activate
41406          * Fires when this tab becomes the active tab.
41407          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41408          * @param {Roo.TabPanelItem} this
41409          */
41410         "activate": true,
41411         /**
41412          * @event beforeclose
41413          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41414          * @param {Roo.TabPanelItem} this
41415          * @param {Object} e Set cancel to true on this object to cancel the close.
41416          */
41417         "beforeclose": true,
41418         /**
41419          * @event close
41420          * Fires when this tab is closed.
41421          * @param {Roo.TabPanelItem} this
41422          */
41423          "close": true,
41424         /**
41425          * @event deactivate
41426          * Fires when this tab is no longer the active tab.
41427          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41428          * @param {Roo.TabPanelItem} this
41429          */
41430          "deactivate" : true
41431     });
41432     this.hidden = false;
41433
41434     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41435 };
41436
41437 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41438            {
41439     purgeListeners : function(){
41440        Roo.util.Observable.prototype.purgeListeners.call(this);
41441        this.el.removeAllListeners();
41442     },
41443     /**
41444      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41445      */
41446     show : function(){
41447         this.status_node.addClass("active");
41448         this.showAction();
41449         if(Roo.isOpera){
41450             this.tabPanel.stripWrap.repaint();
41451         }
41452         this.fireEvent("activate", this.tabPanel, this);
41453     },
41454
41455     /**
41456      * Returns true if this tab is the active tab.
41457      * @return {Boolean}
41458      */
41459     isActive : function(){
41460         return this.tabPanel.getActiveTab() == this;
41461     },
41462
41463     /**
41464      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41465      */
41466     hide : function(){
41467         this.status_node.removeClass("active");
41468         this.hideAction();
41469         this.fireEvent("deactivate", this.tabPanel, this);
41470     },
41471
41472     hideAction : function(){
41473         this.bodyEl.hide();
41474         this.bodyEl.setStyle("position", "absolute");
41475         this.bodyEl.setLeft("-20000px");
41476         this.bodyEl.setTop("-20000px");
41477     },
41478
41479     showAction : function(){
41480         this.bodyEl.setStyle("position", "relative");
41481         this.bodyEl.setTop("");
41482         this.bodyEl.setLeft("");
41483         this.bodyEl.show();
41484     },
41485
41486     /**
41487      * Set the tooltip for the tab.
41488      * @param {String} tooltip The tab's tooltip
41489      */
41490     setTooltip : function(text){
41491         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41492             this.textEl.dom.qtip = text;
41493             this.textEl.dom.removeAttribute('title');
41494         }else{
41495             this.textEl.dom.title = text;
41496         }
41497     },
41498
41499     onTabClick : function(e){
41500         e.preventDefault();
41501         this.tabPanel.activate(this.id);
41502     },
41503
41504     onTabMouseDown : function(e){
41505         e.preventDefault();
41506         this.tabPanel.activate(this.id);
41507     },
41508 /*
41509     getWidth : function(){
41510         return this.inner.getWidth();
41511     },
41512
41513     setWidth : function(width){
41514         var iwidth = width - this.linode.getPadding("lr");
41515         this.inner.setWidth(iwidth);
41516         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41517         this.linode.setWidth(width);
41518     },
41519 */
41520     /**
41521      * Show or hide the tab
41522      * @param {Boolean} hidden True to hide or false to show.
41523      */
41524     setHidden : function(hidden){
41525         this.hidden = hidden;
41526         this.linode.setStyle("display", hidden ? "none" : "");
41527     },
41528
41529     /**
41530      * Returns true if this tab is "hidden"
41531      * @return {Boolean}
41532      */
41533     isHidden : function(){
41534         return this.hidden;
41535     },
41536
41537     /**
41538      * Returns the text for this tab
41539      * @return {String}
41540      */
41541     getText : function(){
41542         return this.text;
41543     },
41544     /*
41545     autoSize : function(){
41546         //this.el.beginMeasure();
41547         this.textEl.setWidth(1);
41548         /*
41549          *  #2804 [new] Tabs in Roojs
41550          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41551          */
41552         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41553         //this.el.endMeasure();
41554     //},
41555
41556     /**
41557      * Sets the text for the tab (Note: this also sets the tooltip text)
41558      * @param {String} text The tab's text and tooltip
41559      */
41560     setText : function(text){
41561         this.text = text;
41562         this.textEl.update(text);
41563         this.setTooltip(text);
41564         //if(!this.tabPanel.resizeTabs){
41565         //    this.autoSize();
41566         //}
41567     },
41568     /**
41569      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41570      */
41571     activate : function(){
41572         this.tabPanel.activate(this.id);
41573     },
41574
41575     /**
41576      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41577      */
41578     disable : function(){
41579         if(this.tabPanel.active != this){
41580             this.disabled = true;
41581             this.status_node.addClass("disabled");
41582         }
41583     },
41584
41585     /**
41586      * Enables this TabPanelItem if it was previously disabled.
41587      */
41588     enable : function(){
41589         this.disabled = false;
41590         this.status_node.removeClass("disabled");
41591     },
41592
41593     /**
41594      * Sets the content for this TabPanelItem.
41595      * @param {String} content The content
41596      * @param {Boolean} loadScripts true to look for and load scripts
41597      */
41598     setContent : function(content, loadScripts){
41599         this.bodyEl.update(content, loadScripts);
41600     },
41601
41602     /**
41603      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41604      * @return {Roo.UpdateManager} The UpdateManager
41605      */
41606     getUpdateManager : function(){
41607         return this.bodyEl.getUpdateManager();
41608     },
41609
41610     /**
41611      * Set a URL to be used to load the content for this TabPanelItem.
41612      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41613      * @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)
41614      * @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)
41615      * @return {Roo.UpdateManager} The UpdateManager
41616      */
41617     setUrl : function(url, params, loadOnce){
41618         if(this.refreshDelegate){
41619             this.un('activate', this.refreshDelegate);
41620         }
41621         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41622         this.on("activate", this.refreshDelegate);
41623         return this.bodyEl.getUpdateManager();
41624     },
41625
41626     /** @private */
41627     _handleRefresh : function(url, params, loadOnce){
41628         if(!loadOnce || !this.loaded){
41629             var updater = this.bodyEl.getUpdateManager();
41630             updater.update(url, params, this._setLoaded.createDelegate(this));
41631         }
41632     },
41633
41634     /**
41635      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41636      *   Will fail silently if the setUrl method has not been called.
41637      *   This does not activate the panel, just updates its content.
41638      */
41639     refresh : function(){
41640         if(this.refreshDelegate){
41641            this.loaded = false;
41642            this.refreshDelegate();
41643         }
41644     },
41645
41646     /** @private */
41647     _setLoaded : function(){
41648         this.loaded = true;
41649     },
41650
41651     /** @private */
41652     closeClick : function(e){
41653         var o = {};
41654         e.stopEvent();
41655         this.fireEvent("beforeclose", this, o);
41656         if(o.cancel !== true){
41657             this.tabPanel.removeTab(this.id);
41658         }
41659     },
41660     /**
41661      * The text displayed in the tooltip for the close icon.
41662      * @type String
41663      */
41664     closeText : "Close this tab"
41665 });
41666 /**
41667 *    This script refer to:
41668 *    Title: International Telephone Input
41669 *    Author: Jack O'Connor
41670 *    Code version:  v12.1.12
41671 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41672 **/
41673
41674 Roo.bootstrap.PhoneInputData = function() {
41675     var d = [
41676       [
41677         "Afghanistan (‫افغانستان‬‎)",
41678         "af",
41679         "93"
41680       ],
41681       [
41682         "Albania (Shqipëri)",
41683         "al",
41684         "355"
41685       ],
41686       [
41687         "Algeria (‫الجزائر‬‎)",
41688         "dz",
41689         "213"
41690       ],
41691       [
41692         "American Samoa",
41693         "as",
41694         "1684"
41695       ],
41696       [
41697         "Andorra",
41698         "ad",
41699         "376"
41700       ],
41701       [
41702         "Angola",
41703         "ao",
41704         "244"
41705       ],
41706       [
41707         "Anguilla",
41708         "ai",
41709         "1264"
41710       ],
41711       [
41712         "Antigua and Barbuda",
41713         "ag",
41714         "1268"
41715       ],
41716       [
41717         "Argentina",
41718         "ar",
41719         "54"
41720       ],
41721       [
41722         "Armenia (Հայաստան)",
41723         "am",
41724         "374"
41725       ],
41726       [
41727         "Aruba",
41728         "aw",
41729         "297"
41730       ],
41731       [
41732         "Australia",
41733         "au",
41734         "61",
41735         0
41736       ],
41737       [
41738         "Austria (Österreich)",
41739         "at",
41740         "43"
41741       ],
41742       [
41743         "Azerbaijan (Azərbaycan)",
41744         "az",
41745         "994"
41746       ],
41747       [
41748         "Bahamas",
41749         "bs",
41750         "1242"
41751       ],
41752       [
41753         "Bahrain (‫البحرين‬‎)",
41754         "bh",
41755         "973"
41756       ],
41757       [
41758         "Bangladesh (বাংলাদেশ)",
41759         "bd",
41760         "880"
41761       ],
41762       [
41763         "Barbados",
41764         "bb",
41765         "1246"
41766       ],
41767       [
41768         "Belarus (Беларусь)",
41769         "by",
41770         "375"
41771       ],
41772       [
41773         "Belgium (België)",
41774         "be",
41775         "32"
41776       ],
41777       [
41778         "Belize",
41779         "bz",
41780         "501"
41781       ],
41782       [
41783         "Benin (Bénin)",
41784         "bj",
41785         "229"
41786       ],
41787       [
41788         "Bermuda",
41789         "bm",
41790         "1441"
41791       ],
41792       [
41793         "Bhutan (འབྲུག)",
41794         "bt",
41795         "975"
41796       ],
41797       [
41798         "Bolivia",
41799         "bo",
41800         "591"
41801       ],
41802       [
41803         "Bosnia and Herzegovina (Босна и Херцеговина)",
41804         "ba",
41805         "387"
41806       ],
41807       [
41808         "Botswana",
41809         "bw",
41810         "267"
41811       ],
41812       [
41813         "Brazil (Brasil)",
41814         "br",
41815         "55"
41816       ],
41817       [
41818         "British Indian Ocean Territory",
41819         "io",
41820         "246"
41821       ],
41822       [
41823         "British Virgin Islands",
41824         "vg",
41825         "1284"
41826       ],
41827       [
41828         "Brunei",
41829         "bn",
41830         "673"
41831       ],
41832       [
41833         "Bulgaria (България)",
41834         "bg",
41835         "359"
41836       ],
41837       [
41838         "Burkina Faso",
41839         "bf",
41840         "226"
41841       ],
41842       [
41843         "Burundi (Uburundi)",
41844         "bi",
41845         "257"
41846       ],
41847       [
41848         "Cambodia (កម្ពុជា)",
41849         "kh",
41850         "855"
41851       ],
41852       [
41853         "Cameroon (Cameroun)",
41854         "cm",
41855         "237"
41856       ],
41857       [
41858         "Canada",
41859         "ca",
41860         "1",
41861         1,
41862         ["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"]
41863       ],
41864       [
41865         "Cape Verde (Kabu Verdi)",
41866         "cv",
41867         "238"
41868       ],
41869       [
41870         "Caribbean Netherlands",
41871         "bq",
41872         "599",
41873         1
41874       ],
41875       [
41876         "Cayman Islands",
41877         "ky",
41878         "1345"
41879       ],
41880       [
41881         "Central African Republic (République centrafricaine)",
41882         "cf",
41883         "236"
41884       ],
41885       [
41886         "Chad (Tchad)",
41887         "td",
41888         "235"
41889       ],
41890       [
41891         "Chile",
41892         "cl",
41893         "56"
41894       ],
41895       [
41896         "China (中国)",
41897         "cn",
41898         "86"
41899       ],
41900       [
41901         "Christmas Island",
41902         "cx",
41903         "61",
41904         2
41905       ],
41906       [
41907         "Cocos (Keeling) Islands",
41908         "cc",
41909         "61",
41910         1
41911       ],
41912       [
41913         "Colombia",
41914         "co",
41915         "57"
41916       ],
41917       [
41918         "Comoros (‫جزر القمر‬‎)",
41919         "km",
41920         "269"
41921       ],
41922       [
41923         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41924         "cd",
41925         "243"
41926       ],
41927       [
41928         "Congo (Republic) (Congo-Brazzaville)",
41929         "cg",
41930         "242"
41931       ],
41932       [
41933         "Cook Islands",
41934         "ck",
41935         "682"
41936       ],
41937       [
41938         "Costa Rica",
41939         "cr",
41940         "506"
41941       ],
41942       [
41943         "Côte d’Ivoire",
41944         "ci",
41945         "225"
41946       ],
41947       [
41948         "Croatia (Hrvatska)",
41949         "hr",
41950         "385"
41951       ],
41952       [
41953         "Cuba",
41954         "cu",
41955         "53"
41956       ],
41957       [
41958         "Curaçao",
41959         "cw",
41960         "599",
41961         0
41962       ],
41963       [
41964         "Cyprus (Κύπρος)",
41965         "cy",
41966         "357"
41967       ],
41968       [
41969         "Czech Republic (Česká republika)",
41970         "cz",
41971         "420"
41972       ],
41973       [
41974         "Denmark (Danmark)",
41975         "dk",
41976         "45"
41977       ],
41978       [
41979         "Djibouti",
41980         "dj",
41981         "253"
41982       ],
41983       [
41984         "Dominica",
41985         "dm",
41986         "1767"
41987       ],
41988       [
41989         "Dominican Republic (República Dominicana)",
41990         "do",
41991         "1",
41992         2,
41993         ["809", "829", "849"]
41994       ],
41995       [
41996         "Ecuador",
41997         "ec",
41998         "593"
41999       ],
42000       [
42001         "Egypt (‫مصر‬‎)",
42002         "eg",
42003         "20"
42004       ],
42005       [
42006         "El Salvador",
42007         "sv",
42008         "503"
42009       ],
42010       [
42011         "Equatorial Guinea (Guinea Ecuatorial)",
42012         "gq",
42013         "240"
42014       ],
42015       [
42016         "Eritrea",
42017         "er",
42018         "291"
42019       ],
42020       [
42021         "Estonia (Eesti)",
42022         "ee",
42023         "372"
42024       ],
42025       [
42026         "Ethiopia",
42027         "et",
42028         "251"
42029       ],
42030       [
42031         "Falkland Islands (Islas Malvinas)",
42032         "fk",
42033         "500"
42034       ],
42035       [
42036         "Faroe Islands (Føroyar)",
42037         "fo",
42038         "298"
42039       ],
42040       [
42041         "Fiji",
42042         "fj",
42043         "679"
42044       ],
42045       [
42046         "Finland (Suomi)",
42047         "fi",
42048         "358",
42049         0
42050       ],
42051       [
42052         "France",
42053         "fr",
42054         "33"
42055       ],
42056       [
42057         "French Guiana (Guyane française)",
42058         "gf",
42059         "594"
42060       ],
42061       [
42062         "French Polynesia (Polynésie française)",
42063         "pf",
42064         "689"
42065       ],
42066       [
42067         "Gabon",
42068         "ga",
42069         "241"
42070       ],
42071       [
42072         "Gambia",
42073         "gm",
42074         "220"
42075       ],
42076       [
42077         "Georgia (საქართველო)",
42078         "ge",
42079         "995"
42080       ],
42081       [
42082         "Germany (Deutschland)",
42083         "de",
42084         "49"
42085       ],
42086       [
42087         "Ghana (Gaana)",
42088         "gh",
42089         "233"
42090       ],
42091       [
42092         "Gibraltar",
42093         "gi",
42094         "350"
42095       ],
42096       [
42097         "Greece (Ελλάδα)",
42098         "gr",
42099         "30"
42100       ],
42101       [
42102         "Greenland (Kalaallit Nunaat)",
42103         "gl",
42104         "299"
42105       ],
42106       [
42107         "Grenada",
42108         "gd",
42109         "1473"
42110       ],
42111       [
42112         "Guadeloupe",
42113         "gp",
42114         "590",
42115         0
42116       ],
42117       [
42118         "Guam",
42119         "gu",
42120         "1671"
42121       ],
42122       [
42123         "Guatemala",
42124         "gt",
42125         "502"
42126       ],
42127       [
42128         "Guernsey",
42129         "gg",
42130         "44",
42131         1
42132       ],
42133       [
42134         "Guinea (Guinée)",
42135         "gn",
42136         "224"
42137       ],
42138       [
42139         "Guinea-Bissau (Guiné Bissau)",
42140         "gw",
42141         "245"
42142       ],
42143       [
42144         "Guyana",
42145         "gy",
42146         "592"
42147       ],
42148       [
42149         "Haiti",
42150         "ht",
42151         "509"
42152       ],
42153       [
42154         "Honduras",
42155         "hn",
42156         "504"
42157       ],
42158       [
42159         "Hong Kong (香港)",
42160         "hk",
42161         "852"
42162       ],
42163       [
42164         "Hungary (Magyarország)",
42165         "hu",
42166         "36"
42167       ],
42168       [
42169         "Iceland (Ísland)",
42170         "is",
42171         "354"
42172       ],
42173       [
42174         "India (भारत)",
42175         "in",
42176         "91"
42177       ],
42178       [
42179         "Indonesia",
42180         "id",
42181         "62"
42182       ],
42183       [
42184         "Iran (‫ایران‬‎)",
42185         "ir",
42186         "98"
42187       ],
42188       [
42189         "Iraq (‫العراق‬‎)",
42190         "iq",
42191         "964"
42192       ],
42193       [
42194         "Ireland",
42195         "ie",
42196         "353"
42197       ],
42198       [
42199         "Isle of Man",
42200         "im",
42201         "44",
42202         2
42203       ],
42204       [
42205         "Israel (‫ישראל‬‎)",
42206         "il",
42207         "972"
42208       ],
42209       [
42210         "Italy (Italia)",
42211         "it",
42212         "39",
42213         0
42214       ],
42215       [
42216         "Jamaica",
42217         "jm",
42218         "1876"
42219       ],
42220       [
42221         "Japan (日本)",
42222         "jp",
42223         "81"
42224       ],
42225       [
42226         "Jersey",
42227         "je",
42228         "44",
42229         3
42230       ],
42231       [
42232         "Jordan (‫الأردن‬‎)",
42233         "jo",
42234         "962"
42235       ],
42236       [
42237         "Kazakhstan (Казахстан)",
42238         "kz",
42239         "7",
42240         1
42241       ],
42242       [
42243         "Kenya",
42244         "ke",
42245         "254"
42246       ],
42247       [
42248         "Kiribati",
42249         "ki",
42250         "686"
42251       ],
42252       [
42253         "Kosovo",
42254         "xk",
42255         "383"
42256       ],
42257       [
42258         "Kuwait (‫الكويت‬‎)",
42259         "kw",
42260         "965"
42261       ],
42262       [
42263         "Kyrgyzstan (Кыргызстан)",
42264         "kg",
42265         "996"
42266       ],
42267       [
42268         "Laos (ລາວ)",
42269         "la",
42270         "856"
42271       ],
42272       [
42273         "Latvia (Latvija)",
42274         "lv",
42275         "371"
42276       ],
42277       [
42278         "Lebanon (‫لبنان‬‎)",
42279         "lb",
42280         "961"
42281       ],
42282       [
42283         "Lesotho",
42284         "ls",
42285         "266"
42286       ],
42287       [
42288         "Liberia",
42289         "lr",
42290         "231"
42291       ],
42292       [
42293         "Libya (‫ليبيا‬‎)",
42294         "ly",
42295         "218"
42296       ],
42297       [
42298         "Liechtenstein",
42299         "li",
42300         "423"
42301       ],
42302       [
42303         "Lithuania (Lietuva)",
42304         "lt",
42305         "370"
42306       ],
42307       [
42308         "Luxembourg",
42309         "lu",
42310         "352"
42311       ],
42312       [
42313         "Macau (澳門)",
42314         "mo",
42315         "853"
42316       ],
42317       [
42318         "Macedonia (FYROM) (Македонија)",
42319         "mk",
42320         "389"
42321       ],
42322       [
42323         "Madagascar (Madagasikara)",
42324         "mg",
42325         "261"
42326       ],
42327       [
42328         "Malawi",
42329         "mw",
42330         "265"
42331       ],
42332       [
42333         "Malaysia",
42334         "my",
42335         "60"
42336       ],
42337       [
42338         "Maldives",
42339         "mv",
42340         "960"
42341       ],
42342       [
42343         "Mali",
42344         "ml",
42345         "223"
42346       ],
42347       [
42348         "Malta",
42349         "mt",
42350         "356"
42351       ],
42352       [
42353         "Marshall Islands",
42354         "mh",
42355         "692"
42356       ],
42357       [
42358         "Martinique",
42359         "mq",
42360         "596"
42361       ],
42362       [
42363         "Mauritania (‫موريتانيا‬‎)",
42364         "mr",
42365         "222"
42366       ],
42367       [
42368         "Mauritius (Moris)",
42369         "mu",
42370         "230"
42371       ],
42372       [
42373         "Mayotte",
42374         "yt",
42375         "262",
42376         1
42377       ],
42378       [
42379         "Mexico (México)",
42380         "mx",
42381         "52"
42382       ],
42383       [
42384         "Micronesia",
42385         "fm",
42386         "691"
42387       ],
42388       [
42389         "Moldova (Republica Moldova)",
42390         "md",
42391         "373"
42392       ],
42393       [
42394         "Monaco",
42395         "mc",
42396         "377"
42397       ],
42398       [
42399         "Mongolia (Монгол)",
42400         "mn",
42401         "976"
42402       ],
42403       [
42404         "Montenegro (Crna Gora)",
42405         "me",
42406         "382"
42407       ],
42408       [
42409         "Montserrat",
42410         "ms",
42411         "1664"
42412       ],
42413       [
42414         "Morocco (‫المغرب‬‎)",
42415         "ma",
42416         "212",
42417         0
42418       ],
42419       [
42420         "Mozambique (Moçambique)",
42421         "mz",
42422         "258"
42423       ],
42424       [
42425         "Myanmar (Burma) (မြန်မာ)",
42426         "mm",
42427         "95"
42428       ],
42429       [
42430         "Namibia (Namibië)",
42431         "na",
42432         "264"
42433       ],
42434       [
42435         "Nauru",
42436         "nr",
42437         "674"
42438       ],
42439       [
42440         "Nepal (नेपाल)",
42441         "np",
42442         "977"
42443       ],
42444       [
42445         "Netherlands (Nederland)",
42446         "nl",
42447         "31"
42448       ],
42449       [
42450         "New Caledonia (Nouvelle-Calédonie)",
42451         "nc",
42452         "687"
42453       ],
42454       [
42455         "New Zealand",
42456         "nz",
42457         "64"
42458       ],
42459       [
42460         "Nicaragua",
42461         "ni",
42462         "505"
42463       ],
42464       [
42465         "Niger (Nijar)",
42466         "ne",
42467         "227"
42468       ],
42469       [
42470         "Nigeria",
42471         "ng",
42472         "234"
42473       ],
42474       [
42475         "Niue",
42476         "nu",
42477         "683"
42478       ],
42479       [
42480         "Norfolk Island",
42481         "nf",
42482         "672"
42483       ],
42484       [
42485         "North Korea (조선 민주주의 인민 공화국)",
42486         "kp",
42487         "850"
42488       ],
42489       [
42490         "Northern Mariana Islands",
42491         "mp",
42492         "1670"
42493       ],
42494       [
42495         "Norway (Norge)",
42496         "no",
42497         "47",
42498         0
42499       ],
42500       [
42501         "Oman (‫عُمان‬‎)",
42502         "om",
42503         "968"
42504       ],
42505       [
42506         "Pakistan (‫پاکستان‬‎)",
42507         "pk",
42508         "92"
42509       ],
42510       [
42511         "Palau",
42512         "pw",
42513         "680"
42514       ],
42515       [
42516         "Palestine (‫فلسطين‬‎)",
42517         "ps",
42518         "970"
42519       ],
42520       [
42521         "Panama (Panamá)",
42522         "pa",
42523         "507"
42524       ],
42525       [
42526         "Papua New Guinea",
42527         "pg",
42528         "675"
42529       ],
42530       [
42531         "Paraguay",
42532         "py",
42533         "595"
42534       ],
42535       [
42536         "Peru (Perú)",
42537         "pe",
42538         "51"
42539       ],
42540       [
42541         "Philippines",
42542         "ph",
42543         "63"
42544       ],
42545       [
42546         "Poland (Polska)",
42547         "pl",
42548         "48"
42549       ],
42550       [
42551         "Portugal",
42552         "pt",
42553         "351"
42554       ],
42555       [
42556         "Puerto Rico",
42557         "pr",
42558         "1",
42559         3,
42560         ["787", "939"]
42561       ],
42562       [
42563         "Qatar (‫قطر‬‎)",
42564         "qa",
42565         "974"
42566       ],
42567       [
42568         "Réunion (La Réunion)",
42569         "re",
42570         "262",
42571         0
42572       ],
42573       [
42574         "Romania (România)",
42575         "ro",
42576         "40"
42577       ],
42578       [
42579         "Russia (Россия)",
42580         "ru",
42581         "7",
42582         0
42583       ],
42584       [
42585         "Rwanda",
42586         "rw",
42587         "250"
42588       ],
42589       [
42590         "Saint Barthélemy",
42591         "bl",
42592         "590",
42593         1
42594       ],
42595       [
42596         "Saint Helena",
42597         "sh",
42598         "290"
42599       ],
42600       [
42601         "Saint Kitts and Nevis",
42602         "kn",
42603         "1869"
42604       ],
42605       [
42606         "Saint Lucia",
42607         "lc",
42608         "1758"
42609       ],
42610       [
42611         "Saint Martin (Saint-Martin (partie française))",
42612         "mf",
42613         "590",
42614         2
42615       ],
42616       [
42617         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42618         "pm",
42619         "508"
42620       ],
42621       [
42622         "Saint Vincent and the Grenadines",
42623         "vc",
42624         "1784"
42625       ],
42626       [
42627         "Samoa",
42628         "ws",
42629         "685"
42630       ],
42631       [
42632         "San Marino",
42633         "sm",
42634         "378"
42635       ],
42636       [
42637         "São Tomé and Príncipe (São Tomé e Príncipe)",
42638         "st",
42639         "239"
42640       ],
42641       [
42642         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42643         "sa",
42644         "966"
42645       ],
42646       [
42647         "Senegal (Sénégal)",
42648         "sn",
42649         "221"
42650       ],
42651       [
42652         "Serbia (Србија)",
42653         "rs",
42654         "381"
42655       ],
42656       [
42657         "Seychelles",
42658         "sc",
42659         "248"
42660       ],
42661       [
42662         "Sierra Leone",
42663         "sl",
42664         "232"
42665       ],
42666       [
42667         "Singapore",
42668         "sg",
42669         "65"
42670       ],
42671       [
42672         "Sint Maarten",
42673         "sx",
42674         "1721"
42675       ],
42676       [
42677         "Slovakia (Slovensko)",
42678         "sk",
42679         "421"
42680       ],
42681       [
42682         "Slovenia (Slovenija)",
42683         "si",
42684         "386"
42685       ],
42686       [
42687         "Solomon Islands",
42688         "sb",
42689         "677"
42690       ],
42691       [
42692         "Somalia (Soomaaliya)",
42693         "so",
42694         "252"
42695       ],
42696       [
42697         "South Africa",
42698         "za",
42699         "27"
42700       ],
42701       [
42702         "South Korea (대한민국)",
42703         "kr",
42704         "82"
42705       ],
42706       [
42707         "South Sudan (‫جنوب السودان‬‎)",
42708         "ss",
42709         "211"
42710       ],
42711       [
42712         "Spain (España)",
42713         "es",
42714         "34"
42715       ],
42716       [
42717         "Sri Lanka (ශ්‍රී ලංකාව)",
42718         "lk",
42719         "94"
42720       ],
42721       [
42722         "Sudan (‫السودان‬‎)",
42723         "sd",
42724         "249"
42725       ],
42726       [
42727         "Suriname",
42728         "sr",
42729         "597"
42730       ],
42731       [
42732         "Svalbard and Jan Mayen",
42733         "sj",
42734         "47",
42735         1
42736       ],
42737       [
42738         "Swaziland",
42739         "sz",
42740         "268"
42741       ],
42742       [
42743         "Sweden (Sverige)",
42744         "se",
42745         "46"
42746       ],
42747       [
42748         "Switzerland (Schweiz)",
42749         "ch",
42750         "41"
42751       ],
42752       [
42753         "Syria (‫سوريا‬‎)",
42754         "sy",
42755         "963"
42756       ],
42757       [
42758         "Taiwan (台灣)",
42759         "tw",
42760         "886"
42761       ],
42762       [
42763         "Tajikistan",
42764         "tj",
42765         "992"
42766       ],
42767       [
42768         "Tanzania",
42769         "tz",
42770         "255"
42771       ],
42772       [
42773         "Thailand (ไทย)",
42774         "th",
42775         "66"
42776       ],
42777       [
42778         "Timor-Leste",
42779         "tl",
42780         "670"
42781       ],
42782       [
42783         "Togo",
42784         "tg",
42785         "228"
42786       ],
42787       [
42788         "Tokelau",
42789         "tk",
42790         "690"
42791       ],
42792       [
42793         "Tonga",
42794         "to",
42795         "676"
42796       ],
42797       [
42798         "Trinidad and Tobago",
42799         "tt",
42800         "1868"
42801       ],
42802       [
42803         "Tunisia (‫تونس‬‎)",
42804         "tn",
42805         "216"
42806       ],
42807       [
42808         "Turkey (Türkiye)",
42809         "tr",
42810         "90"
42811       ],
42812       [
42813         "Turkmenistan",
42814         "tm",
42815         "993"
42816       ],
42817       [
42818         "Turks and Caicos Islands",
42819         "tc",
42820         "1649"
42821       ],
42822       [
42823         "Tuvalu",
42824         "tv",
42825         "688"
42826       ],
42827       [
42828         "U.S. Virgin Islands",
42829         "vi",
42830         "1340"
42831       ],
42832       [
42833         "Uganda",
42834         "ug",
42835         "256"
42836       ],
42837       [
42838         "Ukraine (Україна)",
42839         "ua",
42840         "380"
42841       ],
42842       [
42843         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42844         "ae",
42845         "971"
42846       ],
42847       [
42848         "United Kingdom",
42849         "gb",
42850         "44",
42851         0
42852       ],
42853       [
42854         "United States",
42855         "us",
42856         "1",
42857         0
42858       ],
42859       [
42860         "Uruguay",
42861         "uy",
42862         "598"
42863       ],
42864       [
42865         "Uzbekistan (Oʻzbekiston)",
42866         "uz",
42867         "998"
42868       ],
42869       [
42870         "Vanuatu",
42871         "vu",
42872         "678"
42873       ],
42874       [
42875         "Vatican City (Città del Vaticano)",
42876         "va",
42877         "39",
42878         1
42879       ],
42880       [
42881         "Venezuela",
42882         "ve",
42883         "58"
42884       ],
42885       [
42886         "Vietnam (Việt Nam)",
42887         "vn",
42888         "84"
42889       ],
42890       [
42891         "Wallis and Futuna (Wallis-et-Futuna)",
42892         "wf",
42893         "681"
42894       ],
42895       [
42896         "Western Sahara (‫الصحراء الغربية‬‎)",
42897         "eh",
42898         "212",
42899         1
42900       ],
42901       [
42902         "Yemen (‫اليمن‬‎)",
42903         "ye",
42904         "967"
42905       ],
42906       [
42907         "Zambia",
42908         "zm",
42909         "260"
42910       ],
42911       [
42912         "Zimbabwe",
42913         "zw",
42914         "263"
42915       ],
42916       [
42917         "Åland Islands",
42918         "ax",
42919         "358",
42920         1
42921       ]
42922   ];
42923   
42924   return d;
42925 }/**
42926 *    This script refer to:
42927 *    Title: International Telephone Input
42928 *    Author: Jack O'Connor
42929 *    Code version:  v12.1.12
42930 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42931 **/
42932
42933 /**
42934  * @class Roo.bootstrap.PhoneInput
42935  * @extends Roo.bootstrap.TriggerField
42936  * An input with International dial-code selection
42937  
42938  * @cfg {String} defaultDialCode default '+852'
42939  * @cfg {Array} preferedCountries default []
42940   
42941  * @constructor
42942  * Create a new PhoneInput.
42943  * @param {Object} config Configuration options
42944  */
42945
42946 Roo.bootstrap.PhoneInput = function(config) {
42947     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42948 };
42949
42950 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42951         
42952         listWidth: undefined,
42953         
42954         selectedClass: 'active',
42955         
42956         invalidClass : "has-warning",
42957         
42958         validClass: 'has-success',
42959         
42960         allowed: '0123456789',
42961         
42962         max_length: 15,
42963         
42964         /**
42965          * @cfg {String} defaultDialCode The default dial code when initializing the input
42966          */
42967         defaultDialCode: '+852',
42968         
42969         /**
42970          * @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
42971          */
42972         preferedCountries: false,
42973         
42974         getAutoCreate : function()
42975         {
42976             var data = Roo.bootstrap.PhoneInputData();
42977             var align = this.labelAlign || this.parentLabelAlign();
42978             var id = Roo.id();
42979             
42980             this.allCountries = [];
42981             this.dialCodeMapping = [];
42982             
42983             for (var i = 0; i < data.length; i++) {
42984               var c = data[i];
42985               this.allCountries[i] = {
42986                 name: c[0],
42987                 iso2: c[1],
42988                 dialCode: c[2],
42989                 priority: c[3] || 0,
42990                 areaCodes: c[4] || null
42991               };
42992               this.dialCodeMapping[c[2]] = {
42993                   name: c[0],
42994                   iso2: c[1],
42995                   priority: c[3] || 0,
42996                   areaCodes: c[4] || null
42997               };
42998             }
42999             
43000             var cfg = {
43001                 cls: 'form-group',
43002                 cn: []
43003             };
43004             
43005             var input =  {
43006                 tag: 'input',
43007                 id : id,
43008                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43009                 maxlength: this.max_length,
43010                 cls : 'form-control tel-input',
43011                 autocomplete: 'new-password'
43012             };
43013             
43014             var hiddenInput = {
43015                 tag: 'input',
43016                 type: 'hidden',
43017                 cls: 'hidden-tel-input'
43018             };
43019             
43020             if (this.name) {
43021                 hiddenInput.name = this.name;
43022             }
43023             
43024             if (this.disabled) {
43025                 input.disabled = true;
43026             }
43027             
43028             var flag_container = {
43029                 tag: 'div',
43030                 cls: 'flag-box',
43031                 cn: [
43032                     {
43033                         tag: 'div',
43034                         cls: 'flag'
43035                     },
43036                     {
43037                         tag: 'div',
43038                         cls: 'caret'
43039                     }
43040                 ]
43041             };
43042             
43043             var box = {
43044                 tag: 'div',
43045                 cls: this.hasFeedback ? 'has-feedback' : '',
43046                 cn: [
43047                     hiddenInput,
43048                     input,
43049                     {
43050                         tag: 'input',
43051                         cls: 'dial-code-holder',
43052                         disabled: true
43053                     }
43054                 ]
43055             };
43056             
43057             var container = {
43058                 cls: 'roo-select2-container input-group',
43059                 cn: [
43060                     flag_container,
43061                     box
43062                 ]
43063             };
43064             
43065             if (this.fieldLabel.length) {
43066                 var indicator = {
43067                     tag: 'i',
43068                     tooltip: 'This field is required'
43069                 };
43070                 
43071                 var label = {
43072                     tag: 'label',
43073                     'for':  id,
43074                     cls: 'control-label',
43075                     cn: []
43076                 };
43077                 
43078                 var label_text = {
43079                     tag: 'span',
43080                     html: this.fieldLabel
43081                 };
43082                 
43083                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43084                 label.cn = [
43085                     indicator,
43086                     label_text
43087                 ];
43088                 
43089                 if(this.indicatorpos == 'right') {
43090                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43091                     label.cn = [
43092                         label_text,
43093                         indicator
43094                     ];
43095                 }
43096                 
43097                 if(align == 'left') {
43098                     container = {
43099                         tag: 'div',
43100                         cn: [
43101                             container
43102                         ]
43103                     };
43104                     
43105                     if(this.labelWidth > 12){
43106                         label.style = "width: " + this.labelWidth + 'px';
43107                     }
43108                     if(this.labelWidth < 13 && this.labelmd == 0){
43109                         this.labelmd = this.labelWidth;
43110                     }
43111                     if(this.labellg > 0){
43112                         label.cls += ' col-lg-' + this.labellg;
43113                         input.cls += ' col-lg-' + (12 - this.labellg);
43114                     }
43115                     if(this.labelmd > 0){
43116                         label.cls += ' col-md-' + this.labelmd;
43117                         container.cls += ' col-md-' + (12 - this.labelmd);
43118                     }
43119                     if(this.labelsm > 0){
43120                         label.cls += ' col-sm-' + this.labelsm;
43121                         container.cls += ' col-sm-' + (12 - this.labelsm);
43122                     }
43123                     if(this.labelxs > 0){
43124                         label.cls += ' col-xs-' + this.labelxs;
43125                         container.cls += ' col-xs-' + (12 - this.labelxs);
43126                     }
43127                 }
43128             }
43129             
43130             cfg.cn = [
43131                 label,
43132                 container
43133             ];
43134             
43135             var settings = this;
43136             
43137             ['xs','sm','md','lg'].map(function(size){
43138                 if (settings[size]) {
43139                     cfg.cls += ' col-' + size + '-' + settings[size];
43140                 }
43141             });
43142             
43143             this.store = new Roo.data.Store({
43144                 proxy : new Roo.data.MemoryProxy({}),
43145                 reader : new Roo.data.JsonReader({
43146                     fields : [
43147                         {
43148                             'name' : 'name',
43149                             'type' : 'string'
43150                         },
43151                         {
43152                             'name' : 'iso2',
43153                             'type' : 'string'
43154                         },
43155                         {
43156                             'name' : 'dialCode',
43157                             'type' : 'string'
43158                         },
43159                         {
43160                             'name' : 'priority',
43161                             'type' : 'string'
43162                         },
43163                         {
43164                             'name' : 'areaCodes',
43165                             'type' : 'string'
43166                         }
43167                     ]
43168                 })
43169             });
43170             
43171             if(!this.preferedCountries) {
43172                 this.preferedCountries = [
43173                     'hk',
43174                     'gb',
43175                     'us'
43176                 ];
43177             }
43178             
43179             var p = this.preferedCountries.reverse();
43180             
43181             if(p) {
43182                 for (var i = 0; i < p.length; i++) {
43183                     for (var j = 0; j < this.allCountries.length; j++) {
43184                         if(this.allCountries[j].iso2 == p[i]) {
43185                             var t = this.allCountries[j];
43186                             this.allCountries.splice(j,1);
43187                             this.allCountries.unshift(t);
43188                         }
43189                     } 
43190                 }
43191             }
43192             
43193             this.store.proxy.data = {
43194                 success: true,
43195                 data: this.allCountries
43196             };
43197             
43198             return cfg;
43199         },
43200         
43201         initEvents : function()
43202         {
43203             this.createList();
43204             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43205             
43206             this.indicator = this.indicatorEl();
43207             this.flag = this.flagEl();
43208             this.dialCodeHolder = this.dialCodeHolderEl();
43209             
43210             this.trigger = this.el.select('div.flag-box',true).first();
43211             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43212             
43213             var _this = this;
43214             
43215             (function(){
43216                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43217                 _this.list.setWidth(lw);
43218             }).defer(100);
43219             
43220             this.list.on('mouseover', this.onViewOver, this);
43221             this.list.on('mousemove', this.onViewMove, this);
43222             this.inputEl().on("keyup", this.onKeyUp, this);
43223             this.inputEl().on("keypress", this.onKeyPress, this);
43224             
43225             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43226
43227             this.view = new Roo.View(this.list, this.tpl, {
43228                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43229             });
43230             
43231             this.view.on('click', this.onViewClick, this);
43232             this.setValue(this.defaultDialCode);
43233         },
43234         
43235         onTriggerClick : function(e)
43236         {
43237             Roo.log('trigger click');
43238             if(this.disabled){
43239                 return;
43240             }
43241             
43242             if(this.isExpanded()){
43243                 this.collapse();
43244                 this.hasFocus = false;
43245             }else {
43246                 this.store.load({});
43247                 this.hasFocus = true;
43248                 this.expand();
43249             }
43250         },
43251         
43252         isExpanded : function()
43253         {
43254             return this.list.isVisible();
43255         },
43256         
43257         collapse : function()
43258         {
43259             if(!this.isExpanded()){
43260                 return;
43261             }
43262             this.list.hide();
43263             Roo.get(document).un('mousedown', this.collapseIf, this);
43264             Roo.get(document).un('mousewheel', this.collapseIf, this);
43265             this.fireEvent('collapse', this);
43266             this.validate();
43267         },
43268         
43269         expand : function()
43270         {
43271             Roo.log('expand');
43272
43273             if(this.isExpanded() || !this.hasFocus){
43274                 return;
43275             }
43276             
43277             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43278             this.list.setWidth(lw);
43279             
43280             this.list.show();
43281             this.restrictHeight();
43282             
43283             Roo.get(document).on('mousedown', this.collapseIf, this);
43284             Roo.get(document).on('mousewheel', this.collapseIf, this);
43285             
43286             this.fireEvent('expand', this);
43287         },
43288         
43289         restrictHeight : function()
43290         {
43291             this.list.alignTo(this.inputEl(), this.listAlign);
43292             this.list.alignTo(this.inputEl(), this.listAlign);
43293         },
43294         
43295         onViewOver : function(e, t)
43296         {
43297             if(this.inKeyMode){
43298                 return;
43299             }
43300             var item = this.view.findItemFromChild(t);
43301             
43302             if(item){
43303                 var index = this.view.indexOf(item);
43304                 this.select(index, false);
43305             }
43306         },
43307
43308         // private
43309         onViewClick : function(view, doFocus, el, e)
43310         {
43311             var index = this.view.getSelectedIndexes()[0];
43312             
43313             var r = this.store.getAt(index);
43314             
43315             if(r){
43316                 this.onSelect(r, index);
43317             }
43318             if(doFocus !== false && !this.blockFocus){
43319                 this.inputEl().focus();
43320             }
43321         },
43322         
43323         onViewMove : function(e, t)
43324         {
43325             this.inKeyMode = false;
43326         },
43327         
43328         select : function(index, scrollIntoView)
43329         {
43330             this.selectedIndex = index;
43331             this.view.select(index);
43332             if(scrollIntoView !== false){
43333                 var el = this.view.getNode(index);
43334                 if(el){
43335                     this.list.scrollChildIntoView(el, false);
43336                 }
43337             }
43338         },
43339         
43340         createList : function()
43341         {
43342             this.list = Roo.get(document.body).createChild({
43343                 tag: 'ul',
43344                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43345                 style: 'display:none'
43346             });
43347             
43348             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43349         },
43350         
43351         collapseIf : function(e)
43352         {
43353             var in_combo  = e.within(this.el);
43354             var in_list =  e.within(this.list);
43355             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43356             
43357             if (in_combo || in_list || is_list) {
43358                 return;
43359             }
43360             this.collapse();
43361         },
43362         
43363         onSelect : function(record, index)
43364         {
43365             if(this.fireEvent('beforeselect', this, record, index) !== false){
43366                 
43367                 this.setFlagClass(record.data.iso2);
43368                 this.setDialCode(record.data.dialCode);
43369                 this.hasFocus = false;
43370                 this.collapse();
43371                 this.fireEvent('select', this, record, index);
43372             }
43373         },
43374         
43375         flagEl : function()
43376         {
43377             var flag = this.el.select('div.flag',true).first();
43378             if(!flag){
43379                 return false;
43380             }
43381             return flag;
43382         },
43383         
43384         dialCodeHolderEl : function()
43385         {
43386             var d = this.el.select('input.dial-code-holder',true).first();
43387             if(!d){
43388                 return false;
43389             }
43390             return d;
43391         },
43392         
43393         setDialCode : function(v)
43394         {
43395             this.dialCodeHolder.dom.value = '+'+v;
43396         },
43397         
43398         setFlagClass : function(n)
43399         {
43400             this.flag.dom.className = 'flag '+n;
43401         },
43402         
43403         getValue : function()
43404         {
43405             var v = this.inputEl().getValue();
43406             if(this.dialCodeHolder) {
43407                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43408             }
43409             return v;
43410         },
43411         
43412         setValue : function(v)
43413         {
43414             var d = this.getDialCode(v);
43415             
43416             //invalid dial code
43417             if(v.length == 0 || !d || d.length == 0) {
43418                 if(this.rendered){
43419                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43420                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43421                 }
43422                 return;
43423             }
43424             
43425             //valid dial code
43426             this.setFlagClass(this.dialCodeMapping[d].iso2);
43427             this.setDialCode(d);
43428             this.inputEl().dom.value = v.replace('+'+d,'');
43429             this.hiddenEl().dom.value = this.getValue();
43430             
43431             this.validate();
43432         },
43433         
43434         getDialCode : function(v)
43435         {
43436             v = v ||  '';
43437             
43438             if (v.length == 0) {
43439                 return this.dialCodeHolder.dom.value;
43440             }
43441             
43442             var dialCode = "";
43443             if (v.charAt(0) != "+") {
43444                 return false;
43445             }
43446             var numericChars = "";
43447             for (var i = 1; i < v.length; i++) {
43448               var c = v.charAt(i);
43449               if (!isNaN(c)) {
43450                 numericChars += c;
43451                 if (this.dialCodeMapping[numericChars]) {
43452                   dialCode = v.substr(1, i);
43453                 }
43454                 if (numericChars.length == 4) {
43455                   break;
43456                 }
43457               }
43458             }
43459             return dialCode;
43460         },
43461         
43462         reset : function()
43463         {
43464             this.setValue(this.defaultDialCode);
43465             this.validate();
43466         },
43467         
43468         hiddenEl : function()
43469         {
43470             return this.el.select('input.hidden-tel-input',true).first();
43471         },
43472         
43473         // after setting val
43474         onKeyUp : function(e){
43475             this.setValue(this.getValue());
43476         },
43477         
43478         onKeyPress : function(e){
43479             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43480                 e.stopEvent();
43481             }
43482         }
43483         
43484 });
43485 /**
43486  * @class Roo.bootstrap.MoneyField
43487  * @extends Roo.bootstrap.ComboBox
43488  * Bootstrap MoneyField class
43489  * 
43490  * @constructor
43491  * Create a new MoneyField.
43492  * @param {Object} config Configuration options
43493  */
43494
43495 Roo.bootstrap.MoneyField = function(config) {
43496     
43497     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43498     
43499 };
43500
43501 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43502     
43503     /**
43504      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43505      */
43506     allowDecimals : true,
43507     /**
43508      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43509      */
43510     decimalSeparator : ".",
43511     /**
43512      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43513      */
43514     decimalPrecision : 0,
43515     /**
43516      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43517      */
43518     allowNegative : true,
43519     /**
43520      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43521      */
43522     allowZero: true,
43523     /**
43524      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43525      */
43526     minValue : Number.NEGATIVE_INFINITY,
43527     /**
43528      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43529      */
43530     maxValue : Number.MAX_VALUE,
43531     /**
43532      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43533      */
43534     minText : "The minimum value for this field is {0}",
43535     /**
43536      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43537      */
43538     maxText : "The maximum value for this field is {0}",
43539     /**
43540      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43541      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43542      */
43543     nanText : "{0} is not a valid number",
43544     /**
43545      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43546      */
43547     castInt : true,
43548     /**
43549      * @cfg {String} defaults currency of the MoneyField
43550      * value should be in lkey
43551      */
43552     defaultCurrency : false,
43553     /**
43554      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43555      */
43556     thousandsDelimiter : false,
43557     /**
43558      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43559      */
43560     max_length: false,
43561     
43562     inputlg : 9,
43563     inputmd : 9,
43564     inputsm : 9,
43565     inputxs : 6,
43566     
43567     store : false,
43568     
43569     getAutoCreate : function()
43570     {
43571         var align = this.labelAlign || this.parentLabelAlign();
43572         
43573         var id = Roo.id();
43574
43575         var cfg = {
43576             cls: 'form-group',
43577             cn: []
43578         };
43579
43580         var input =  {
43581             tag: 'input',
43582             id : id,
43583             cls : 'form-control roo-money-amount-input',
43584             autocomplete: 'new-password'
43585         };
43586         
43587         var hiddenInput = {
43588             tag: 'input',
43589             type: 'hidden',
43590             id: Roo.id(),
43591             cls: 'hidden-number-input'
43592         };
43593         
43594         if(this.max_length) {
43595             input.maxlength = this.max_length; 
43596         }
43597         
43598         if (this.name) {
43599             hiddenInput.name = this.name;
43600         }
43601
43602         if (this.disabled) {
43603             input.disabled = true;
43604         }
43605
43606         var clg = 12 - this.inputlg;
43607         var cmd = 12 - this.inputmd;
43608         var csm = 12 - this.inputsm;
43609         var cxs = 12 - this.inputxs;
43610         
43611         var container = {
43612             tag : 'div',
43613             cls : 'row roo-money-field',
43614             cn : [
43615                 {
43616                     tag : 'div',
43617                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43618                     cn : [
43619                         {
43620                             tag : 'div',
43621                             cls: 'roo-select2-container input-group',
43622                             cn: [
43623                                 {
43624                                     tag : 'input',
43625                                     cls : 'form-control roo-money-currency-input',
43626                                     autocomplete: 'new-password',
43627                                     readOnly : 1,
43628                                     name : this.currencyName
43629                                 },
43630                                 {
43631                                     tag :'span',
43632                                     cls : 'input-group-addon',
43633                                     cn : [
43634                                         {
43635                                             tag: 'span',
43636                                             cls: 'caret'
43637                                         }
43638                                     ]
43639                                 }
43640                             ]
43641                         }
43642                     ]
43643                 },
43644                 {
43645                     tag : 'div',
43646                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43647                     cn : [
43648                         {
43649                             tag: 'div',
43650                             cls: this.hasFeedback ? 'has-feedback' : '',
43651                             cn: [
43652                                 input
43653                             ]
43654                         }
43655                     ]
43656                 }
43657             ]
43658             
43659         };
43660         
43661         if (this.fieldLabel.length) {
43662             var indicator = {
43663                 tag: 'i',
43664                 tooltip: 'This field is required'
43665             };
43666
43667             var label = {
43668                 tag: 'label',
43669                 'for':  id,
43670                 cls: 'control-label',
43671                 cn: []
43672             };
43673
43674             var label_text = {
43675                 tag: 'span',
43676                 html: this.fieldLabel
43677             };
43678
43679             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43680             label.cn = [
43681                 indicator,
43682                 label_text
43683             ];
43684
43685             if(this.indicatorpos == 'right') {
43686                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43687                 label.cn = [
43688                     label_text,
43689                     indicator
43690                 ];
43691             }
43692
43693             if(align == 'left') {
43694                 container = {
43695                     tag: 'div',
43696                     cn: [
43697                         container
43698                     ]
43699                 };
43700
43701                 if(this.labelWidth > 12){
43702                     label.style = "width: " + this.labelWidth + 'px';
43703                 }
43704                 if(this.labelWidth < 13 && this.labelmd == 0){
43705                     this.labelmd = this.labelWidth;
43706                 }
43707                 if(this.labellg > 0){
43708                     label.cls += ' col-lg-' + this.labellg;
43709                     input.cls += ' col-lg-' + (12 - this.labellg);
43710                 }
43711                 if(this.labelmd > 0){
43712                     label.cls += ' col-md-' + this.labelmd;
43713                     container.cls += ' col-md-' + (12 - this.labelmd);
43714                 }
43715                 if(this.labelsm > 0){
43716                     label.cls += ' col-sm-' + this.labelsm;
43717                     container.cls += ' col-sm-' + (12 - this.labelsm);
43718                 }
43719                 if(this.labelxs > 0){
43720                     label.cls += ' col-xs-' + this.labelxs;
43721                     container.cls += ' col-xs-' + (12 - this.labelxs);
43722                 }
43723             }
43724         }
43725
43726         cfg.cn = [
43727             label,
43728             container,
43729             hiddenInput
43730         ];
43731         
43732         var settings = this;
43733
43734         ['xs','sm','md','lg'].map(function(size){
43735             if (settings[size]) {
43736                 cfg.cls += ' col-' + size + '-' + settings[size];
43737             }
43738         });
43739         
43740         return cfg;
43741     },
43742     
43743     initEvents : function()
43744     {
43745         this.indicator = this.indicatorEl();
43746         
43747         this.initCurrencyEvent();
43748         
43749         this.initNumberEvent();
43750     },
43751     
43752     initCurrencyEvent : function()
43753     {
43754         if (!this.store) {
43755             throw "can not find store for combo";
43756         }
43757         
43758         this.store = Roo.factory(this.store, Roo.data);
43759         this.store.parent = this;
43760         
43761         this.createList();
43762         
43763         this.triggerEl = this.el.select('.input-group-addon', true).first();
43764         
43765         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43766         
43767         var _this = this;
43768         
43769         (function(){
43770             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43771             _this.list.setWidth(lw);
43772         }).defer(100);
43773         
43774         this.list.on('mouseover', this.onViewOver, this);
43775         this.list.on('mousemove', this.onViewMove, this);
43776         this.list.on('scroll', this.onViewScroll, this);
43777         
43778         if(!this.tpl){
43779             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43780         }
43781         
43782         this.view = new Roo.View(this.list, this.tpl, {
43783             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43784         });
43785         
43786         this.view.on('click', this.onViewClick, this);
43787         
43788         this.store.on('beforeload', this.onBeforeLoad, this);
43789         this.store.on('load', this.onLoad, this);
43790         this.store.on('loadexception', this.onLoadException, this);
43791         
43792         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43793             "up" : function(e){
43794                 this.inKeyMode = true;
43795                 this.selectPrev();
43796             },
43797
43798             "down" : function(e){
43799                 if(!this.isExpanded()){
43800                     this.onTriggerClick();
43801                 }else{
43802                     this.inKeyMode = true;
43803                     this.selectNext();
43804                 }
43805             },
43806
43807             "enter" : function(e){
43808                 this.collapse();
43809                 
43810                 if(this.fireEvent("specialkey", this, e)){
43811                     this.onViewClick(false);
43812                 }
43813                 
43814                 return true;
43815             },
43816
43817             "esc" : function(e){
43818                 this.collapse();
43819             },
43820
43821             "tab" : function(e){
43822                 this.collapse();
43823                 
43824                 if(this.fireEvent("specialkey", this, e)){
43825                     this.onViewClick(false);
43826                 }
43827                 
43828                 return true;
43829             },
43830
43831             scope : this,
43832
43833             doRelay : function(foo, bar, hname){
43834                 if(hname == 'down' || this.scope.isExpanded()){
43835                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43836                 }
43837                 return true;
43838             },
43839
43840             forceKeyDown: true
43841         });
43842         
43843         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43844         
43845     },
43846     
43847     initNumberEvent : function(e)
43848     {
43849         this.inputEl().on("keydown" , this.fireKey,  this);
43850         this.inputEl().on("focus", this.onFocus,  this);
43851         this.inputEl().on("blur", this.onBlur,  this);
43852         
43853         this.inputEl().relayEvent('keyup', this);
43854         
43855         if(this.indicator){
43856             this.indicator.addClass('invisible');
43857         }
43858  
43859         this.originalValue = this.getValue();
43860         
43861         if(this.validationEvent == 'keyup'){
43862             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43863             this.inputEl().on('keyup', this.filterValidation, this);
43864         }
43865         else if(this.validationEvent !== false){
43866             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43867         }
43868         
43869         if(this.selectOnFocus){
43870             this.on("focus", this.preFocus, this);
43871             
43872         }
43873         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43874             this.inputEl().on("keypress", this.filterKeys, this);
43875         } else {
43876             this.inputEl().relayEvent('keypress', this);
43877         }
43878         
43879         var allowed = "0123456789";
43880         
43881         if(this.allowDecimals){
43882             allowed += this.decimalSeparator;
43883         }
43884         
43885         if(this.allowNegative){
43886             allowed += "-";
43887         }
43888         
43889         if(this.thousandsDelimiter) {
43890             allowed += ",";
43891         }
43892         
43893         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43894         
43895         var keyPress = function(e){
43896             
43897             var k = e.getKey();
43898             
43899             var c = e.getCharCode();
43900             
43901             if(
43902                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43903                     allowed.indexOf(String.fromCharCode(c)) === -1
43904             ){
43905                 e.stopEvent();
43906                 return;
43907             }
43908             
43909             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43910                 return;
43911             }
43912             
43913             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43914                 e.stopEvent();
43915             }
43916         };
43917         
43918         this.inputEl().on("keypress", keyPress, this);
43919         
43920     },
43921     
43922     onTriggerClick : function(e)
43923     {   
43924         if(this.disabled){
43925             return;
43926         }
43927         
43928         this.page = 0;
43929         this.loadNext = false;
43930         
43931         if(this.isExpanded()){
43932             this.collapse();
43933             return;
43934         }
43935         
43936         this.hasFocus = true;
43937         
43938         if(this.triggerAction == 'all') {
43939             this.doQuery(this.allQuery, true);
43940             return;
43941         }
43942         
43943         this.doQuery(this.getRawValue());
43944     },
43945     
43946     getCurrency : function()
43947     {   
43948         var v = this.currencyEl().getValue();
43949         
43950         return v;
43951     },
43952     
43953     restrictHeight : function()
43954     {
43955         this.list.alignTo(this.currencyEl(), this.listAlign);
43956         this.list.alignTo(this.currencyEl(), this.listAlign);
43957     },
43958     
43959     onViewClick : function(view, doFocus, el, e)
43960     {
43961         var index = this.view.getSelectedIndexes()[0];
43962         
43963         var r = this.store.getAt(index);
43964         
43965         if(r){
43966             this.onSelect(r, index);
43967         }
43968     },
43969     
43970     onSelect : function(record, index){
43971         
43972         if(this.fireEvent('beforeselect', this, record, index) !== false){
43973         
43974             this.setFromCurrencyData(index > -1 ? record.data : false);
43975             
43976             this.collapse();
43977             
43978             this.fireEvent('select', this, record, index);
43979         }
43980     },
43981     
43982     setFromCurrencyData : function(o)
43983     {
43984         var currency = '';
43985         
43986         this.lastCurrency = o;
43987         
43988         if (this.currencyField) {
43989             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43990         } else {
43991             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43992         }
43993         
43994         this.lastSelectionText = currency;
43995         
43996         //setting default currency
43997         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43998             this.setCurrency(this.defaultCurrency);
43999             return;
44000         }
44001         
44002         this.setCurrency(currency);
44003     },
44004     
44005     setFromData : function(o)
44006     {
44007         var c = {};
44008         
44009         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44010         
44011         this.setFromCurrencyData(c);
44012         
44013         var value = '';
44014         
44015         if (this.name) {
44016             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44017         } else {
44018             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44019         }
44020         
44021         this.setValue(value);
44022         
44023     },
44024     
44025     setCurrency : function(v)
44026     {   
44027         this.currencyValue = v;
44028         
44029         if(this.rendered){
44030             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44031             this.validate();
44032         }
44033     },
44034     
44035     setValue : function(v)
44036     {
44037         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44038         
44039         this.value = v;
44040         
44041         if(this.rendered){
44042             
44043             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44044             
44045             this.inputEl().dom.value = (v == '') ? '' :
44046                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44047             
44048             if(!this.allowZero && v === '0') {
44049                 this.hiddenEl().dom.value = '';
44050                 this.inputEl().dom.value = '';
44051             }
44052             
44053             this.validate();
44054         }
44055     },
44056     
44057     getRawValue : function()
44058     {
44059         var v = this.inputEl().getValue();
44060         
44061         return v;
44062     },
44063     
44064     getValue : function()
44065     {
44066         return this.fixPrecision(this.parseValue(this.getRawValue()));
44067     },
44068     
44069     parseValue : function(value)
44070     {
44071         if(this.thousandsDelimiter) {
44072             value += "";
44073             r = new RegExp(",", "g");
44074             value = value.replace(r, "");
44075         }
44076         
44077         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44078         return isNaN(value) ? '' : value;
44079         
44080     },
44081     
44082     fixPrecision : function(value)
44083     {
44084         if(this.thousandsDelimiter) {
44085             value += "";
44086             r = new RegExp(",", "g");
44087             value = value.replace(r, "");
44088         }
44089         
44090         var nan = isNaN(value);
44091         
44092         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44093             return nan ? '' : value;
44094         }
44095         return parseFloat(value).toFixed(this.decimalPrecision);
44096     },
44097     
44098     decimalPrecisionFcn : function(v)
44099     {
44100         return Math.floor(v);
44101     },
44102     
44103     validateValue : function(value)
44104     {
44105         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44106             return false;
44107         }
44108         
44109         var num = this.parseValue(value);
44110         
44111         if(isNaN(num)){
44112             this.markInvalid(String.format(this.nanText, value));
44113             return false;
44114         }
44115         
44116         if(num < this.minValue){
44117             this.markInvalid(String.format(this.minText, this.minValue));
44118             return false;
44119         }
44120         
44121         if(num > this.maxValue){
44122             this.markInvalid(String.format(this.maxText, this.maxValue));
44123             return false;
44124         }
44125         
44126         return true;
44127     },
44128     
44129     validate : function()
44130     {
44131         if(this.disabled || this.allowBlank){
44132             this.markValid();
44133             return true;
44134         }
44135         
44136         var currency = this.getCurrency();
44137         
44138         if(this.validateValue(this.getRawValue()) && currency.length){
44139             this.markValid();
44140             return true;
44141         }
44142         
44143         this.markInvalid();
44144         return false;
44145     },
44146     
44147     getName: function()
44148     {
44149         return this.name;
44150     },
44151     
44152     beforeBlur : function()
44153     {
44154         if(!this.castInt){
44155             return;
44156         }
44157         
44158         var v = this.parseValue(this.getRawValue());
44159         
44160         if(v || v == 0){
44161             this.setValue(v);
44162         }
44163     },
44164     
44165     onBlur : function()
44166     {
44167         this.beforeBlur();
44168         
44169         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44170             //this.el.removeClass(this.focusClass);
44171         }
44172         
44173         this.hasFocus = false;
44174         
44175         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44176             this.validate();
44177         }
44178         
44179         var v = this.getValue();
44180         
44181         if(String(v) !== String(this.startValue)){
44182             this.fireEvent('change', this, v, this.startValue);
44183         }
44184         
44185         this.fireEvent("blur", this);
44186     },
44187     
44188     inputEl : function()
44189     {
44190         return this.el.select('.roo-money-amount-input', true).first();
44191     },
44192     
44193     currencyEl : function()
44194     {
44195         return this.el.select('.roo-money-currency-input', true).first();
44196     },
44197     
44198     hiddenEl : function()
44199     {
44200         return this.el.select('input.hidden-number-input',true).first();
44201     }
44202     
44203 });/**
44204  * @class Roo.bootstrap.BezierSignature
44205  * @extends Roo.bootstrap.Component
44206  * Bootstrap BezierSignature class
44207  * This script refer to:
44208  *    Title: Signature Pad
44209  *    Author: szimek
44210  *    Availability: https://github.com/szimek/signature_pad
44211  *
44212  * @constructor
44213  * Create a new BezierSignature
44214  * @param {Object} config The config object
44215  */
44216
44217 Roo.bootstrap.BezierSignature = function(config){
44218     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44219     this.addEvents({
44220         "resize" : true
44221     });
44222 };
44223
44224 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44225 {
44226      
44227     curve_data: [],
44228     
44229     is_empty: true,
44230     
44231     mouse_btn_down: true,
44232     
44233     /**
44234      * @cfg {int} canvas height
44235      */
44236     canvas_height: '200px',
44237     
44238     /**
44239      * @cfg {float|function} Radius of a single dot.
44240      */ 
44241     dot_size: false,
44242     
44243     /**
44244      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44245      */
44246     min_width: 0.5,
44247     
44248     /**
44249      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44250      */
44251     max_width: 2.5,
44252     
44253     /**
44254      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44255      */
44256     throttle: 16,
44257     
44258     /**
44259      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44260      */
44261     min_distance: 5,
44262     
44263     /**
44264      * @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.
44265      */
44266     bg_color: 'rgba(0, 0, 0, 0)',
44267     
44268     /**
44269      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44270      */
44271     dot_color: 'black',
44272     
44273     /**
44274      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44275      */ 
44276     velocity_filter_weight: 0.7,
44277     
44278     /**
44279      * @cfg {function} Callback when stroke begin. 
44280      */
44281     onBegin: false,
44282     
44283     /**
44284      * @cfg {function} Callback when stroke end.
44285      */
44286     onEnd: false,
44287     
44288     getAutoCreate : function()
44289     {
44290         var cls = 'roo-signature column';
44291         
44292         if(this.cls){
44293             cls += ' ' + this.cls;
44294         }
44295         
44296         var col_sizes = [
44297             'lg',
44298             'md',
44299             'sm',
44300             'xs'
44301         ];
44302         
44303         for(var i = 0; i < col_sizes.length; i++) {
44304             if(this[col_sizes[i]]) {
44305                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44306             }
44307         }
44308         
44309         var cfg = {
44310             tag: 'div',
44311             cls: cls,
44312             cn: [
44313                 {
44314                     tag: 'div',
44315                     cls: 'roo-signature-body',
44316                     cn: [
44317                         {
44318                             tag: 'canvas',
44319                             cls: 'roo-signature-body-canvas',
44320                             height: this.canvas_height,
44321                             width: this.canvas_width
44322                         }
44323                     ]
44324                 },
44325                 {
44326                     tag: 'input',
44327                     type: 'file',
44328                     style: 'display: none'
44329                 }
44330             ]
44331         };
44332         
44333         return cfg;
44334     },
44335     
44336     initEvents: function() 
44337     {
44338         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44339         
44340         var canvas = this.canvasEl();
44341         
44342         // mouse && touch event swapping...
44343         canvas.dom.style.touchAction = 'none';
44344         canvas.dom.style.msTouchAction = 'none';
44345         
44346         this.mouse_btn_down = false;
44347         canvas.on('mousedown', this._handleMouseDown, this);
44348         canvas.on('mousemove', this._handleMouseMove, this);
44349         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44350         
44351         if (window.PointerEvent) {
44352             canvas.on('pointerdown', this._handleMouseDown, this);
44353             canvas.on('pointermove', this._handleMouseMove, this);
44354             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44355         }
44356         
44357         if ('ontouchstart' in window) {
44358             canvas.on('touchstart', this._handleTouchStart, this);
44359             canvas.on('touchmove', this._handleTouchMove, this);
44360             canvas.on('touchend', this._handleTouchEnd, this);
44361         }
44362         
44363         Roo.EventManager.onWindowResize(this.resize, this, true);
44364         
44365         // file input event
44366         this.fileEl().on('change', this.uploadImage, this);
44367         
44368         this.clear();
44369         
44370         this.resize();
44371     },
44372     
44373     resize: function(){
44374         
44375         var canvas = this.canvasEl().dom;
44376         var ctx = this.canvasElCtx();
44377         var img_data = false;
44378         
44379         if(canvas.width > 0) {
44380             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44381         }
44382         // setting canvas width will clean img data
44383         canvas.width = 0;
44384         
44385         var style = window.getComputedStyle ? 
44386             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44387             
44388         var padding_left = parseInt(style.paddingLeft) || 0;
44389         var padding_right = parseInt(style.paddingRight) || 0;
44390         
44391         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44392         
44393         if(img_data) {
44394             ctx.putImageData(img_data, 0, 0);
44395         }
44396     },
44397     
44398     _handleMouseDown: function(e)
44399     {
44400         if (e.browserEvent.which === 1) {
44401             this.mouse_btn_down = true;
44402             this.strokeBegin(e);
44403         }
44404     },
44405     
44406     _handleMouseMove: function (e)
44407     {
44408         if (this.mouse_btn_down) {
44409             this.strokeMoveUpdate(e);
44410         }
44411     },
44412     
44413     _handleMouseUp: function (e)
44414     {
44415         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44416             this.mouse_btn_down = false;
44417             this.strokeEnd(e);
44418         }
44419     },
44420     
44421     _handleTouchStart: function (e) {
44422         
44423         e.preventDefault();
44424         if (e.browserEvent.targetTouches.length === 1) {
44425             // var touch = e.browserEvent.changedTouches[0];
44426             // this.strokeBegin(touch);
44427             
44428              this.strokeBegin(e); // assume e catching the correct xy...
44429         }
44430     },
44431     
44432     _handleTouchMove: function (e) {
44433         e.preventDefault();
44434         // var touch = event.targetTouches[0];
44435         // _this._strokeMoveUpdate(touch);
44436         this.strokeMoveUpdate(e);
44437     },
44438     
44439     _handleTouchEnd: function (e) {
44440         var wasCanvasTouched = e.target === this.canvasEl().dom;
44441         if (wasCanvasTouched) {
44442             e.preventDefault();
44443             // var touch = event.changedTouches[0];
44444             // _this._strokeEnd(touch);
44445             this.strokeEnd(e);
44446         }
44447     },
44448     
44449     reset: function () {
44450         this._lastPoints = [];
44451         this._lastVelocity = 0;
44452         this._lastWidth = (this.min_width + this.max_width) / 2;
44453         this.canvasElCtx().fillStyle = this.dot_color;
44454     },
44455     
44456     strokeMoveUpdate: function(e)
44457     {
44458         this.strokeUpdate(e);
44459         
44460         if (this.throttle) {
44461             this.throttleStroke(this.strokeUpdate, this.throttle);
44462         }
44463         else {
44464             this.strokeUpdate(e);
44465         }
44466     },
44467     
44468     strokeBegin: function(e)
44469     {
44470         var newPointGroup = {
44471             color: this.dot_color,
44472             points: []
44473         };
44474         
44475         if (typeof this.onBegin === 'function') {
44476             this.onBegin(e);
44477         }
44478         
44479         this.curve_data.push(newPointGroup);
44480         this.reset();
44481         this.strokeUpdate(e);
44482     },
44483     
44484     strokeUpdate: function(e)
44485     {
44486         var rect = this.canvasEl().dom.getBoundingClientRect();
44487         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44488         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44489         var lastPoints = lastPointGroup.points;
44490         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44491         var isLastPointTooClose = lastPoint
44492             ? point.distanceTo(lastPoint) <= this.min_distance
44493             : false;
44494         var color = lastPointGroup.color;
44495         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44496             var curve = this.addPoint(point);
44497             if (!lastPoint) {
44498                 this.drawDot({color: color, point: point});
44499             }
44500             else if (curve) {
44501                 this.drawCurve({color: color, curve: curve});
44502             }
44503             lastPoints.push({
44504                 time: point.time,
44505                 x: point.x,
44506                 y: point.y
44507             });
44508         }
44509     },
44510     
44511     strokeEnd: function(e)
44512     {
44513         this.strokeUpdate(e);
44514         if (typeof this.onEnd === 'function') {
44515             this.onEnd(e);
44516         }
44517     },
44518     
44519     addPoint:  function (point) {
44520         var _lastPoints = this._lastPoints;
44521         _lastPoints.push(point);
44522         if (_lastPoints.length > 2) {
44523             if (_lastPoints.length === 3) {
44524                 _lastPoints.unshift(_lastPoints[0]);
44525             }
44526             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44527             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44528             _lastPoints.shift();
44529             return curve;
44530         }
44531         return null;
44532     },
44533     
44534     calculateCurveWidths: function (startPoint, endPoint) {
44535         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44536             (1 - this.velocity_filter_weight) * this._lastVelocity;
44537
44538         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44539         var widths = {
44540             end: newWidth,
44541             start: this._lastWidth
44542         };
44543         
44544         this._lastVelocity = velocity;
44545         this._lastWidth = newWidth;
44546         return widths;
44547     },
44548     
44549     drawDot: function (_a) {
44550         var color = _a.color, point = _a.point;
44551         var ctx = this.canvasElCtx();
44552         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44553         ctx.beginPath();
44554         this.drawCurveSegment(point.x, point.y, width);
44555         ctx.closePath();
44556         ctx.fillStyle = color;
44557         ctx.fill();
44558     },
44559     
44560     drawCurve: function (_a) {
44561         var color = _a.color, curve = _a.curve;
44562         var ctx = this.canvasElCtx();
44563         var widthDelta = curve.endWidth - curve.startWidth;
44564         var drawSteps = Math.floor(curve.length()) * 2;
44565         ctx.beginPath();
44566         ctx.fillStyle = color;
44567         for (var i = 0; i < drawSteps; i += 1) {
44568         var t = i / drawSteps;
44569         var tt = t * t;
44570         var ttt = tt * t;
44571         var u = 1 - t;
44572         var uu = u * u;
44573         var uuu = uu * u;
44574         var x = uuu * curve.startPoint.x;
44575         x += 3 * uu * t * curve.control1.x;
44576         x += 3 * u * tt * curve.control2.x;
44577         x += ttt * curve.endPoint.x;
44578         var y = uuu * curve.startPoint.y;
44579         y += 3 * uu * t * curve.control1.y;
44580         y += 3 * u * tt * curve.control2.y;
44581         y += ttt * curve.endPoint.y;
44582         var width = curve.startWidth + ttt * widthDelta;
44583         this.drawCurveSegment(x, y, width);
44584         }
44585         ctx.closePath();
44586         ctx.fill();
44587     },
44588     
44589     drawCurveSegment: function (x, y, width) {
44590         var ctx = this.canvasElCtx();
44591         ctx.moveTo(x, y);
44592         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44593         this.is_empty = false;
44594     },
44595     
44596     clear: function()
44597     {
44598         var ctx = this.canvasElCtx();
44599         var canvas = this.canvasEl().dom;
44600         ctx.fillStyle = this.bg_color;
44601         ctx.clearRect(0, 0, canvas.width, canvas.height);
44602         ctx.fillRect(0, 0, canvas.width, canvas.height);
44603         this.curve_data = [];
44604         this.reset();
44605         this.is_empty = true;
44606     },
44607     
44608     fileEl: function()
44609     {
44610         return  this.el.select('input',true).first();
44611     },
44612     
44613     canvasEl: function()
44614     {
44615         return this.el.select('canvas',true).first();
44616     },
44617     
44618     canvasElCtx: function()
44619     {
44620         return this.el.select('canvas',true).first().dom.getContext('2d');
44621     },
44622     
44623     getImage: function(type)
44624     {
44625         if(this.is_empty) {
44626             return false;
44627         }
44628         
44629         // encryption ?
44630         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44631     },
44632     
44633     drawFromImage: function(img_src)
44634     {
44635         var img = new Image();
44636         
44637         img.onload = function(){
44638             this.canvasElCtx().drawImage(img, 0, 0);
44639         }.bind(this);
44640         
44641         img.src = img_src;
44642         
44643         this.is_empty = false;
44644     },
44645     
44646     selectImage: function()
44647     {
44648         this.fileEl().dom.click();
44649     },
44650     
44651     uploadImage: function(e)
44652     {
44653         var reader = new FileReader();
44654         
44655         reader.onload = function(e){
44656             var img = new Image();
44657             img.onload = function(){
44658                 this.reset();
44659                 this.canvasElCtx().drawImage(img, 0, 0);
44660             }.bind(this);
44661             img.src = e.target.result;
44662         }.bind(this);
44663         
44664         reader.readAsDataURL(e.target.files[0]);
44665     },
44666     
44667     // Bezier Point Constructor
44668     Point: (function () {
44669         function Point(x, y, time) {
44670             this.x = x;
44671             this.y = y;
44672             this.time = time || Date.now();
44673         }
44674         Point.prototype.distanceTo = function (start) {
44675             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44676         };
44677         Point.prototype.equals = function (other) {
44678             return this.x === other.x && this.y === other.y && this.time === other.time;
44679         };
44680         Point.prototype.velocityFrom = function (start) {
44681             return this.time !== start.time
44682             ? this.distanceTo(start) / (this.time - start.time)
44683             : 0;
44684         };
44685         return Point;
44686     }()),
44687     
44688     
44689     // Bezier Constructor
44690     Bezier: (function () {
44691         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44692             this.startPoint = startPoint;
44693             this.control2 = control2;
44694             this.control1 = control1;
44695             this.endPoint = endPoint;
44696             this.startWidth = startWidth;
44697             this.endWidth = endWidth;
44698         }
44699         Bezier.fromPoints = function (points, widths, scope) {
44700             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44701             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44702             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44703         };
44704         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44705             var dx1 = s1.x - s2.x;
44706             var dy1 = s1.y - s2.y;
44707             var dx2 = s2.x - s3.x;
44708             var dy2 = s2.y - s3.y;
44709             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44710             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44711             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44712             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44713             var dxm = m1.x - m2.x;
44714             var dym = m1.y - m2.y;
44715             var k = l2 / (l1 + l2);
44716             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44717             var tx = s2.x - cm.x;
44718             var ty = s2.y - cm.y;
44719             return {
44720                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44721                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44722             };
44723         };
44724         Bezier.prototype.length = function () {
44725             var steps = 10;
44726             var length = 0;
44727             var px;
44728             var py;
44729             for (var i = 0; i <= steps; i += 1) {
44730                 var t = i / steps;
44731                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44732                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44733                 if (i > 0) {
44734                     var xdiff = cx - px;
44735                     var ydiff = cy - py;
44736                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44737                 }
44738                 px = cx;
44739                 py = cy;
44740             }
44741             return length;
44742         };
44743         Bezier.prototype.point = function (t, start, c1, c2, end) {
44744             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44745             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44746             + (3.0 * c2 * (1.0 - t) * t * t)
44747             + (end * t * t * t);
44748         };
44749         return Bezier;
44750     }()),
44751     
44752     throttleStroke: function(fn, wait) {
44753       if (wait === void 0) { wait = 250; }
44754       var previous = 0;
44755       var timeout = null;
44756       var result;
44757       var storedContext;
44758       var storedArgs;
44759       var later = function () {
44760           previous = Date.now();
44761           timeout = null;
44762           result = fn.apply(storedContext, storedArgs);
44763           if (!timeout) {
44764               storedContext = null;
44765               storedArgs = [];
44766           }
44767       };
44768       return function wrapper() {
44769           var args = [];
44770           for (var _i = 0; _i < arguments.length; _i++) {
44771               args[_i] = arguments[_i];
44772           }
44773           var now = Date.now();
44774           var remaining = wait - (now - previous);
44775           storedContext = this;
44776           storedArgs = args;
44777           if (remaining <= 0 || remaining > wait) {
44778               if (timeout) {
44779                   clearTimeout(timeout);
44780                   timeout = null;
44781               }
44782               previous = now;
44783               result = fn.apply(storedContext, storedArgs);
44784               if (!timeout) {
44785                   storedContext = null;
44786                   storedArgs = [];
44787               }
44788           }
44789           else if (!timeout) {
44790               timeout = window.setTimeout(later, remaining);
44791           }
44792           return result;
44793       };
44794   }
44795   
44796 });
44797
44798  
44799
44800