sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = []; //config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         this.addColumn(config[i]);
7316         
7317     }
7318
7319     /**
7320      * The width of columns which have no width specified (defaults to 100)
7321      * @type Number
7322      */
7323     this.defaultWidth = 100;
7324
7325     /**
7326      * Default sortable of columns which have no sortable specified (defaults to false)
7327      * @type Boolean
7328      */
7329     this.defaultSortable = false;
7330
7331     this.addEvents({
7332         /**
7333              * @event widthchange
7334              * Fires when the width of a column changes.
7335              * @param {ColumnModel} this
7336              * @param {Number} columnIndex The column index
7337              * @param {Number} newWidth The new width
7338              */
7339             "widthchange": true,
7340         /**
7341              * @event headerchange
7342              * Fires when the text of a header changes.
7343              * @param {ColumnModel} this
7344              * @param {Number} columnIndex The column index
7345              * @param {Number} newText The new header text
7346              */
7347             "headerchange": true,
7348         /**
7349              * @event hiddenchange
7350              * Fires when a column is hidden or "unhidden".
7351              * @param {ColumnModel} this
7352              * @param {Number} columnIndex The column index
7353              * @param {Boolean} hidden true if hidden, false otherwise
7354              */
7355             "hiddenchange": true,
7356             /**
7357          * @event columnmoved
7358          * Fires when a column is moved.
7359          * @param {ColumnModel} this
7360          * @param {Number} oldIndex
7361          * @param {Number} newIndex
7362          */
7363         "columnmoved" : true,
7364         /**
7365          * @event columlockchange
7366          * Fires when a column's locked state is changed
7367          * @param {ColumnModel} this
7368          * @param {Number} colIndex
7369          * @param {Boolean} locked true if locked
7370          */
7371         "columnlockchange" : true
7372     });
7373     Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 };
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376     /**
7377      * @cfg {String} header The header text to display in the Grid view.
7378      */
7379     /**
7380      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382      * specified, the column's index is used as an index into the Record's data Array.
7383      */
7384     /**
7385      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7387      */
7388     /**
7389      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390      * Defaults to the value of the {@link #defaultSortable} property.
7391      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7392      */
7393     /**
7394      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7395      */
7396     /**
7397      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7398      */
7399     /**
7400      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7401      */
7402     /**
7403      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7404      */
7405     /**
7406      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7410      */
7411        /**
7412      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7413      */
7414     /**
7415      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7416      */
7417     /**
7418      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7419      */
7420     /**
7421      * @cfg {String} cursor (Optional)
7422      */
7423     /**
7424      * @cfg {String} tooltip (Optional)
7425      */
7426     /**
7427      * @cfg {Number} xs (Optional)
7428      */
7429     /**
7430      * @cfg {Number} sm (Optional)
7431      */
7432     /**
7433      * @cfg {Number} md (Optional)
7434      */
7435     /**
7436      * @cfg {Number} lg (Optional)
7437      */
7438     /**
7439      * Returns the id of the column at the specified index.
7440      * @param {Number} index The column index
7441      * @return {String} the id
7442      */
7443     getColumnId : function(index){
7444         return this.config[index].id;
7445     },
7446
7447     /**
7448      * Returns the column for a specified id.
7449      * @param {String} id The column id
7450      * @return {Object} the column
7451      */
7452     getColumnById : function(id){
7453         return this.lookup[id];
7454     },
7455
7456     
7457     /**
7458      * Returns the column Object for a specified dataIndex.
7459      * @param {String} dataIndex The column dataIndex
7460      * @return {Object|Boolean} the column or false if not found
7461      */
7462     getColumnByDataIndex: function(dataIndex){
7463         var index = this.findColumnIndex(dataIndex);
7464         return index > -1 ? this.config[index] : false;
7465     },
7466     
7467     /**
7468      * Returns the index for a specified column id.
7469      * @param {String} id The column id
7470      * @return {Number} the index, or -1 if not found
7471      */
7472     getIndexById : function(id){
7473         for(var i = 0, len = this.config.length; i < len; i++){
7474             if(this.config[i].id == id){
7475                 return i;
7476             }
7477         }
7478         return -1;
7479     },
7480     
7481     /**
7482      * Returns the index for a specified column dataIndex.
7483      * @param {String} dataIndex The column dataIndex
7484      * @return {Number} the index, or -1 if not found
7485      */
7486     
7487     findColumnIndex : function(dataIndex){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].dataIndex == dataIndex){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     
7497     moveColumn : function(oldIndex, newIndex){
7498         var c = this.config[oldIndex];
7499         this.config.splice(oldIndex, 1);
7500         this.config.splice(newIndex, 0, c);
7501         this.dataMap = null;
7502         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7503     },
7504
7505     isLocked : function(colIndex){
7506         return this.config[colIndex].locked === true;
7507     },
7508
7509     setLocked : function(colIndex, value, suppressEvent){
7510         if(this.isLocked(colIndex) == value){
7511             return;
7512         }
7513         this.config[colIndex].locked = value;
7514         if(!suppressEvent){
7515             this.fireEvent("columnlockchange", this, colIndex, value);
7516         }
7517     },
7518
7519     getTotalLockedWidth : function(){
7520         var totalWidth = 0;
7521         for(var i = 0; i < this.config.length; i++){
7522             if(this.isLocked(i) && !this.isHidden(i)){
7523                 this.totalWidth += this.getColumnWidth(i);
7524             }
7525         }
7526         return totalWidth;
7527     },
7528
7529     getLockedCount : function(){
7530         for(var i = 0, len = this.config.length; i < len; i++){
7531             if(!this.isLocked(i)){
7532                 return i;
7533             }
7534         }
7535         
7536         return this.config.length;
7537     },
7538
7539     /**
7540      * Returns the number of columns.
7541      * @return {Number}
7542      */
7543     getColumnCount : function(visibleOnly){
7544         if(visibleOnly === true){
7545             var c = 0;
7546             for(var i = 0, len = this.config.length; i < len; i++){
7547                 if(!this.isHidden(i)){
7548                     c++;
7549                 }
7550             }
7551             return c;
7552         }
7553         return this.config.length;
7554     },
7555
7556     /**
7557      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558      * @param {Function} fn
7559      * @param {Object} scope (optional)
7560      * @return {Array} result
7561      */
7562     getColumnsBy : function(fn, scope){
7563         var r = [];
7564         for(var i = 0, len = this.config.length; i < len; i++){
7565             var c = this.config[i];
7566             if(fn.call(scope||this, c, i) === true){
7567                 r[r.length] = c;
7568             }
7569         }
7570         return r;
7571     },
7572
7573     /**
7574      * Returns true if the specified column is sortable.
7575      * @param {Number} col The column index
7576      * @return {Boolean}
7577      */
7578     isSortable : function(col){
7579         if(typeof this.config[col].sortable == "undefined"){
7580             return this.defaultSortable;
7581         }
7582         return this.config[col].sortable;
7583     },
7584
7585     /**
7586      * Returns the rendering (formatting) function defined for the column.
7587      * @param {Number} col The column index.
7588      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589      */
7590     getRenderer : function(col){
7591         if(!this.config[col].renderer){
7592             return Roo.grid.ColumnModel.defaultRenderer;
7593         }
7594         return this.config[col].renderer;
7595     },
7596
7597     /**
7598      * Sets the rendering (formatting) function for a column.
7599      * @param {Number} col The column index
7600      * @param {Function} fn The function to use to process the cell's raw data
7601      * to return HTML markup for the grid view. The render function is called with
7602      * the following parameters:<ul>
7603      * <li>Data value.</li>
7604      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605      * <li>css A CSS style string to apply to the table cell.</li>
7606      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608      * <li>Row index</li>
7609      * <li>Column index</li>
7610      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611      */
7612     setRenderer : function(col, fn){
7613         this.config[col].renderer = fn;
7614     },
7615
7616     /**
7617      * Returns the width for the specified column.
7618      * @param {Number} col The column index
7619      * @return {Number}
7620      */
7621     getColumnWidth : function(col){
7622         return this.config[col].width * 1 || this.defaultWidth;
7623     },
7624
7625     /**
7626      * Sets the width for a column.
7627      * @param {Number} col The column index
7628      * @param {Number} width The new width
7629      */
7630     setColumnWidth : function(col, width, suppressEvent){
7631         this.config[col].width = width;
7632         this.totalWidth = null;
7633         if(!suppressEvent){
7634              this.fireEvent("widthchange", this, col, width);
7635         }
7636     },
7637
7638     /**
7639      * Returns the total width of all columns.
7640      * @param {Boolean} includeHidden True to include hidden column widths
7641      * @return {Number}
7642      */
7643     getTotalWidth : function(includeHidden){
7644         if(!this.totalWidth){
7645             this.totalWidth = 0;
7646             for(var i = 0, len = this.config.length; i < len; i++){
7647                 if(includeHidden || !this.isHidden(i)){
7648                     this.totalWidth += this.getColumnWidth(i);
7649                 }
7650             }
7651         }
7652         return this.totalWidth;
7653     },
7654
7655     /**
7656      * Returns the header for the specified column.
7657      * @param {Number} col The column index
7658      * @return {String}
7659      */
7660     getColumnHeader : function(col){
7661         return this.config[col].header;
7662     },
7663
7664     /**
7665      * Sets the header for a column.
7666      * @param {Number} col The column index
7667      * @param {String} header The new header
7668      */
7669     setColumnHeader : function(col, header){
7670         this.config[col].header = header;
7671         this.fireEvent("headerchange", this, col, header);
7672     },
7673
7674     /**
7675      * Returns the tooltip for the specified column.
7676      * @param {Number} col The column index
7677      * @return {String}
7678      */
7679     getColumnTooltip : function(col){
7680             return this.config[col].tooltip;
7681     },
7682     /**
7683      * Sets the tooltip for a column.
7684      * @param {Number} col The column index
7685      * @param {String} tooltip The new tooltip
7686      */
7687     setColumnTooltip : function(col, tooltip){
7688             this.config[col].tooltip = tooltip;
7689     },
7690
7691     /**
7692      * Returns the dataIndex for the specified column.
7693      * @param {Number} col The column index
7694      * @return {Number}
7695      */
7696     getDataIndex : function(col){
7697         return this.config[col].dataIndex;
7698     },
7699
7700     /**
7701      * Sets the dataIndex for a column.
7702      * @param {Number} col The column index
7703      * @param {Number} dataIndex The new dataIndex
7704      */
7705     setDataIndex : function(col, dataIndex){
7706         this.config[col].dataIndex = dataIndex;
7707     },
7708
7709     
7710     
7711     /**
7712      * Returns true if the cell is editable.
7713      * @param {Number} colIndex The column index
7714      * @param {Number} rowIndex The row index - this is nto actually used..?
7715      * @return {Boolean}
7716      */
7717     isCellEditable : function(colIndex, rowIndex){
7718         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7719     },
7720
7721     /**
7722      * Returns the editor defined for the cell/column.
7723      * return false or null to disable editing.
7724      * @param {Number} colIndex The column index
7725      * @param {Number} rowIndex The row index
7726      * @return {Object}
7727      */
7728     getCellEditor : function(colIndex, rowIndex){
7729         return this.config[colIndex].editor;
7730     },
7731
7732     /**
7733      * Sets if a column is editable.
7734      * @param {Number} col The column index
7735      * @param {Boolean} editable True if the column is editable
7736      */
7737     setEditable : function(col, editable){
7738         this.config[col].editable = editable;
7739     },
7740
7741
7742     /**
7743      * Returns true if the column is hidden.
7744      * @param {Number} colIndex The column index
7745      * @return {Boolean}
7746      */
7747     isHidden : function(colIndex){
7748         return this.config[colIndex].hidden;
7749     },
7750
7751
7752     /**
7753      * Returns true if the column width cannot be changed
7754      */
7755     isFixed : function(colIndex){
7756         return this.config[colIndex].fixed;
7757     },
7758
7759     /**
7760      * Returns true if the column can be resized
7761      * @return {Boolean}
7762      */
7763     isResizable : function(colIndex){
7764         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7765     },
7766     /**
7767      * Sets if a column is hidden.
7768      * @param {Number} colIndex The column index
7769      * @param {Boolean} hidden True if the column is hidden
7770      */
7771     setHidden : function(colIndex, hidden){
7772         this.config[colIndex].hidden = hidden;
7773         this.totalWidth = null;
7774         this.fireEvent("hiddenchange", this, colIndex, hidden);
7775     },
7776
7777     /**
7778      * Sets the editor for a column.
7779      * @param {Number} col The column index
7780      * @param {Object} editor The editor object
7781      */
7782     setEditor : function(col, editor){
7783         this.config[col].editor = editor;
7784     },
7785     /**
7786      * Add a column (experimental...) - defaults to adding to the end..
7787      * @param {Object} config 
7788     */
7789     addColumn : function(c)
7790     {
7791     
7792         var i = this.config.length;
7793         this.config[i] = c;
7794         
7795         if(typeof c.dataIndex == "undefined"){
7796             c.dataIndex = i;
7797         }
7798         if(typeof c.renderer == "string"){
7799             c.renderer = Roo.util.Format[c.renderer];
7800         }
7801         if(typeof c.id == "undefined"){
7802             c.id = Roo.id();
7803         }
7804         if(c.editor && c.editor.xtype){
7805             c.editor  = Roo.factory(c.editor, Roo.grid);
7806         }
7807         if(c.editor && c.editor.isFormField){
7808             c.editor = new Roo.grid.GridEditor(c.editor);
7809         }
7810         this.lookup[c.id] = c;
7811     }
7812     
7813 });
7814
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7816 {
7817     if(typeof value == "object") {
7818         return value;
7819     }
7820         if(typeof value == "string" && value.length < 1){
7821             return "&#160;";
7822         }
7823     
7824         return String.format("{0}", value);
7825 };
7826
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7829 /*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839  
7840 /**
7841  * @class Roo.LoadMask
7842  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7843  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7845  * element's UpdateManager load indicator and will be destroyed after the initial load.
7846  * @constructor
7847  * Create a new LoadMask
7848  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849  * @param {Object} config The config object
7850  */
7851 Roo.LoadMask = function(el, config){
7852     this.el = Roo.get(el);
7853     Roo.apply(this, config);
7854     if(this.store){
7855         this.store.on('beforeload', this.onBeforeLoad, this);
7856         this.store.on('load', this.onLoad, this);
7857         this.store.on('loadexception', this.onLoadException, this);
7858         this.removeMask = false;
7859     }else{
7860         var um = this.el.getUpdateManager();
7861         um.showLoadIndicator = false; // disable the default indicator
7862         um.on('beforeupdate', this.onBeforeLoad, this);
7863         um.on('update', this.onLoad, this);
7864         um.on('failure', this.onLoad, this);
7865         this.removeMask = true;
7866     }
7867 };
7868
7869 Roo.LoadMask.prototype = {
7870     /**
7871      * @cfg {Boolean} removeMask
7872      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7874      */
7875     /**
7876      * @cfg {String} msg
7877      * The text to display in a centered loading message box (defaults to 'Loading...')
7878      */
7879     msg : 'Loading...',
7880     /**
7881      * @cfg {String} msgCls
7882      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7883      */
7884     msgCls : 'x-mask-loading',
7885
7886     /**
7887      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7888      * @type Boolean
7889      */
7890     disabled: false,
7891
7892     /**
7893      * Disables the mask to prevent it from being displayed
7894      */
7895     disable : function(){
7896        this.disabled = true;
7897     },
7898
7899     /**
7900      * Enables the mask so that it can be displayed
7901      */
7902     enable : function(){
7903         this.disabled = false;
7904     },
7905     
7906     onLoadException : function()
7907     {
7908         Roo.log(arguments);
7909         
7910         if (typeof(arguments[3]) != 'undefined') {
7911             Roo.MessageBox.alert("Error loading",arguments[3]);
7912         } 
7913         /*
7914         try {
7915             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7917             }   
7918         } catch(e) {
7919             
7920         }
7921         */
7922     
7923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7924     },
7925     // private
7926     onLoad : function()
7927     {
7928         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7929     },
7930
7931     // private
7932     onBeforeLoad : function(){
7933         if(!this.disabled){
7934             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7935         }
7936     },
7937
7938     // private
7939     destroy : function(){
7940         if(this.store){
7941             this.store.un('beforeload', this.onBeforeLoad, this);
7942             this.store.un('load', this.onLoad, this);
7943             this.store.un('loadexception', this.onLoadException, this);
7944         }else{
7945             var um = this.el.getUpdateManager();
7946             um.un('beforeupdate', this.onBeforeLoad, this);
7947             um.un('update', this.onLoad, this);
7948             um.un('failure', this.onLoad, this);
7949         }
7950     }
7951 };/*
7952  * - LGPL
7953  *
7954  * table
7955  * 
7956  */
7957
7958 /**
7959  * @class Roo.bootstrap.Table
7960  * @extends Roo.bootstrap.Component
7961  * Bootstrap Table class
7962  * @cfg {String} cls table class
7963  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964  * @cfg {String} bgcolor Specifies the background color for a table
7965  * @cfg {Number} border Specifies whether the table cells should have borders or not
7966  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967  * @cfg {Number} cellspacing Specifies the space between cells
7968  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970  * @cfg {String} sortable Specifies that the table should be sortable
7971  * @cfg {String} summary Specifies a summary of the content of a table
7972  * @cfg {Number} width Specifies the width of a table
7973  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7974  * 
7975  * @cfg {boolean} striped Should the rows be alternative striped
7976  * @cfg {boolean} bordered Add borders to the table
7977  * @cfg {boolean} hover Add hover highlighting
7978  * @cfg {boolean} condensed Format condensed
7979  * @cfg {boolean} responsive Format condensed
7980  * @cfg {Boolean} loadMask (true|false) default false
7981  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983  * @cfg {Boolean} rowSelection (true|false) default false
7984  * @cfg {Boolean} cellSelection (true|false) default false
7985  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7987  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7988  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7989  
7990  * 
7991  * @constructor
7992  * Create a new Table
7993  * @param {Object} config The config object
7994  */
7995
7996 Roo.bootstrap.Table = function(config){
7997     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7998     
7999   
8000     
8001     // BC...
8002     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8006     
8007     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8008     if (this.sm) {
8009         this.sm.grid = this;
8010         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011         this.sm = this.selModel;
8012         this.sm.xmodule = this.xmodule || false;
8013     }
8014     
8015     if (this.cm && typeof(this.cm.config) == 'undefined') {
8016         this.colModel = new Roo.grid.ColumnModel(this.cm);
8017         this.cm = this.colModel;
8018         this.cm.xmodule = this.xmodule || false;
8019     }
8020     if (this.store) {
8021         this.store= Roo.factory(this.store, Roo.data);
8022         this.ds = this.store;
8023         this.ds.xmodule = this.xmodule || false;
8024          
8025     }
8026     if (this.footer && this.store) {
8027         this.footer.dataSource = this.ds;
8028         this.footer = Roo.factory(this.footer);
8029     }
8030     
8031     /** @private */
8032     this.addEvents({
8033         /**
8034          * @event cellclick
8035          * Fires when a cell is clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Number} columnIndex
8040          * @param {Roo.EventObject} e
8041          */
8042         "cellclick" : true,
8043         /**
8044          * @event celldblclick
8045          * Fires when a cell is double clicked
8046          * @param {Roo.bootstrap.Table} this
8047          * @param {Roo.Element} el
8048          * @param {Number} rowIndex
8049          * @param {Number} columnIndex
8050          * @param {Roo.EventObject} e
8051          */
8052         "celldblclick" : true,
8053         /**
8054          * @event rowclick
8055          * Fires when a row is clicked
8056          * @param {Roo.bootstrap.Table} this
8057          * @param {Roo.Element} el
8058          * @param {Number} rowIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "rowclick" : true,
8062         /**
8063          * @event rowdblclick
8064          * Fires when a row is double clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Roo.Element} el
8067          * @param {Number} rowIndex
8068          * @param {Roo.EventObject} e
8069          */
8070         "rowdblclick" : true,
8071         /**
8072          * @event mouseover
8073          * Fires when a mouseover occur
8074          * @param {Roo.bootstrap.Table} this
8075          * @param {Roo.Element} el
8076          * @param {Number} rowIndex
8077          * @param {Number} columnIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "mouseover" : true,
8081         /**
8082          * @event mouseout
8083          * Fires when a mouseout occur
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Roo.Element} el
8086          * @param {Number} rowIndex
8087          * @param {Number} columnIndex
8088          * @param {Roo.EventObject} e
8089          */
8090         "mouseout" : true,
8091         /**
8092          * @event rowclass
8093          * Fires when a row is rendered, so you can change add a style to it.
8094          * @param {Roo.bootstrap.Table} this
8095          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8096          */
8097         'rowclass' : true,
8098           /**
8099          * @event rowsrendered
8100          * Fires when all the  rows have been rendered
8101          * @param {Roo.bootstrap.Table} this
8102          */
8103         'rowsrendered' : true,
8104         /**
8105          * @event contextmenu
8106          * The raw contextmenu event for the entire grid.
8107          * @param {Roo.EventObject} e
8108          */
8109         "contextmenu" : true,
8110         /**
8111          * @event rowcontextmenu
8112          * Fires when a row is right clicked
8113          * @param {Roo.bootstrap.Table} this
8114          * @param {Number} rowIndex
8115          * @param {Roo.EventObject} e
8116          */
8117         "rowcontextmenu" : true,
8118         /**
8119          * @event cellcontextmenu
8120          * Fires when a cell is right clicked
8121          * @param {Roo.bootstrap.Table} this
8122          * @param {Number} rowIndex
8123          * @param {Number} cellIndex
8124          * @param {Roo.EventObject} e
8125          */
8126          "cellcontextmenu" : true,
8127          /**
8128          * @event headercontextmenu
8129          * Fires when a header is right clicked
8130          * @param {Roo.bootstrap.Table} this
8131          * @param {Number} columnIndex
8132          * @param {Roo.EventObject} e
8133          */
8134         "headercontextmenu" : true
8135     });
8136 };
8137
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8139     
8140     cls: false,
8141     align: false,
8142     bgcolor: false,
8143     border: false,
8144     cellpadding: false,
8145     cellspacing: false,
8146     frame: false,
8147     rules: false,
8148     sortable: false,
8149     summary: false,
8150     width: false,
8151     striped : false,
8152     scrollBody : false,
8153     bordered: false,
8154     hover:  false,
8155     condensed : false,
8156     responsive : false,
8157     sm : false,
8158     cm : false,
8159     store : false,
8160     loadMask : false,
8161     footerShow : true,
8162     headerShow : true,
8163   
8164     rowSelection : false,
8165     cellSelection : false,
8166     layout : false,
8167     
8168     // Roo.Element - the tbody
8169     mainBody: false,
8170     // Roo.Element - thead element
8171     mainHead: false,
8172     
8173     container: false, // used by gridpanel...
8174     
8175     lazyLoad : false,
8176     
8177     CSS : Roo.util.CSS,
8178     
8179     auto_hide_footer : false,
8180     
8181     getAutoCreate : function()
8182     {
8183         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8184         
8185         cfg = {
8186             tag: 'table',
8187             cls : 'table',
8188             cn : []
8189         };
8190         if (this.scrollBody) {
8191             cfg.cls += ' table-body-fixed';
8192         }    
8193         if (this.striped) {
8194             cfg.cls += ' table-striped';
8195         }
8196         
8197         if (this.hover) {
8198             cfg.cls += ' table-hover';
8199         }
8200         if (this.bordered) {
8201             cfg.cls += ' table-bordered';
8202         }
8203         if (this.condensed) {
8204             cfg.cls += ' table-condensed';
8205         }
8206         if (this.responsive) {
8207             cfg.cls += ' table-responsive';
8208         }
8209         
8210         if (this.cls) {
8211             cfg.cls+=  ' ' +this.cls;
8212         }
8213         
8214         // this lot should be simplifed...
8215         var _t = this;
8216         var cp = [
8217             'align',
8218             'bgcolor',
8219             'border',
8220             'cellpadding',
8221             'cellspacing',
8222             'frame',
8223             'rules',
8224             'sortable',
8225             'summary',
8226             'width'
8227         ].forEach(function(k) {
8228             if (_t[k]) {
8229                 cfg[k] = _t[k];
8230             }
8231         });
8232         
8233         
8234         if (this.layout) {
8235             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8236         }
8237         
8238         if(this.store || this.cm){
8239             if(this.headerShow){
8240                 cfg.cn.push(this.renderHeader());
8241             }
8242             
8243             cfg.cn.push(this.renderBody());
8244             
8245             if(this.footerShow){
8246                 cfg.cn.push(this.renderFooter());
8247             }
8248             // where does this come from?
8249             //cfg.cls+=  ' TableGrid';
8250         }
8251         
8252         return { cn : [ cfg ] };
8253     },
8254     
8255     initEvents : function()
8256     {   
8257         if(!this.store || !this.cm){
8258             return;
8259         }
8260         if (this.selModel) {
8261             this.selModel.initEvents();
8262         }
8263         
8264         
8265         //Roo.log('initEvents with ds!!!!');
8266         
8267         this.mainBody = this.el.select('tbody', true).first();
8268         this.mainHead = this.el.select('thead', true).first();
8269         this.mainFoot = this.el.select('tfoot', true).first();
8270         
8271         
8272         
8273         
8274         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275             e.on('click', this.sort, this);
8276         }, this);
8277         
8278         this.mainBody.on("click", this.onClick, this);
8279         this.mainBody.on("dblclick", this.onDblClick, this);
8280         
8281         // why is this done????? = it breaks dialogs??
8282         //this.parent().el.setStyle('position', 'relative');
8283         
8284         
8285         if (this.footer) {
8286             this.footer.parentId = this.id;
8287             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8288             
8289             if(this.lazyLoad){
8290                 this.el.select('tfoot tr td').first().addClass('hide');
8291             }
8292         } 
8293         
8294         if(this.loadMask) {
8295             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8296         }
8297         
8298         this.store.on('load', this.onLoad, this);
8299         this.store.on('beforeload', this.onBeforeLoad, this);
8300         this.store.on('update', this.onUpdate, this);
8301         this.store.on('add', this.onAdd, this);
8302         this.store.on("clear", this.clear, this);
8303         
8304         this.el.on("contextmenu", this.onContextMenu, this);
8305         
8306         this.mainBody.on('scroll', this.onBodyScroll, this);
8307         
8308         this.cm.on("headerchange", this.onHeaderChange, this);
8309         
8310         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8311         
8312     },
8313     
8314     onContextMenu : function(e, t)
8315     {
8316         this.processEvent("contextmenu", e);
8317     },
8318     
8319     processEvent : function(name, e)
8320     {
8321         if (name != 'touchstart' ) {
8322             this.fireEvent(name, e);    
8323         }
8324         
8325         var t = e.getTarget();
8326         
8327         var cell = Roo.get(t);
8328         
8329         if(!cell){
8330             return;
8331         }
8332         
8333         if(cell.findParent('tfoot', false, true)){
8334             return;
8335         }
8336         
8337         if(cell.findParent('thead', false, true)){
8338             
8339             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340                 cell = Roo.get(t).findParent('th', false, true);
8341                 if (!cell) {
8342                     Roo.log("failed to find th in thead?");
8343                     Roo.log(e.getTarget());
8344                     return;
8345                 }
8346             }
8347             
8348             var cellIndex = cell.dom.cellIndex;
8349             
8350             var ename = name == 'touchstart' ? 'click' : name;
8351             this.fireEvent("header" + ename, this, cellIndex, e);
8352             
8353             return;
8354         }
8355         
8356         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357             cell = Roo.get(t).findParent('td', false, true);
8358             if (!cell) {
8359                 Roo.log("failed to find th in tbody?");
8360                 Roo.log(e.getTarget());
8361                 return;
8362             }
8363         }
8364         
8365         var row = cell.findParent('tr', false, true);
8366         var cellIndex = cell.dom.cellIndex;
8367         var rowIndex = row.dom.rowIndex - 1;
8368         
8369         if(row !== false){
8370             
8371             this.fireEvent("row" + name, this, rowIndex, e);
8372             
8373             if(cell !== false){
8374             
8375                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8376             }
8377         }
8378         
8379     },
8380     
8381     onMouseover : function(e, el)
8382     {
8383         var cell = Roo.get(el);
8384         
8385         if(!cell){
8386             return;
8387         }
8388         
8389         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390             cell = cell.findParent('td', false, true);
8391         }
8392         
8393         var row = cell.findParent('tr', false, true);
8394         var cellIndex = cell.dom.cellIndex;
8395         var rowIndex = row.dom.rowIndex - 1; // start from 0
8396         
8397         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8398         
8399     },
8400     
8401     onMouseout : function(e, el)
8402     {
8403         var cell = Roo.get(el);
8404         
8405         if(!cell){
8406             return;
8407         }
8408         
8409         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410             cell = cell.findParent('td', false, true);
8411         }
8412         
8413         var row = cell.findParent('tr', false, true);
8414         var cellIndex = cell.dom.cellIndex;
8415         var rowIndex = row.dom.rowIndex - 1; // start from 0
8416         
8417         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8418         
8419     },
8420     
8421     onClick : function(e, el)
8422     {
8423         var cell = Roo.get(el);
8424         
8425         if(!cell || (!this.cellSelection && !this.rowSelection)){
8426             return;
8427         }
8428         
8429         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430             cell = cell.findParent('td', false, true);
8431         }
8432         
8433         if(!cell || typeof(cell) == 'undefined'){
8434             return;
8435         }
8436         
8437         var row = cell.findParent('tr', false, true);
8438         
8439         if(!row || typeof(row) == 'undefined'){
8440             return;
8441         }
8442         
8443         var cellIndex = cell.dom.cellIndex;
8444         var rowIndex = this.getRowIndex(row);
8445         
8446         // why??? - should these not be based on SelectionModel?
8447         if(this.cellSelection){
8448             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8449         }
8450         
8451         if(this.rowSelection){
8452             this.fireEvent('rowclick', this, row, rowIndex, e);
8453         }
8454         
8455         
8456     },
8457         
8458     onDblClick : function(e,el)
8459     {
8460         var cell = Roo.get(el);
8461         
8462         if(!cell || (!this.cellSelection && !this.rowSelection)){
8463             return;
8464         }
8465         
8466         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467             cell = cell.findParent('td', false, true);
8468         }
8469         
8470         if(!cell || typeof(cell) == 'undefined'){
8471             return;
8472         }
8473         
8474         var row = cell.findParent('tr', false, true);
8475         
8476         if(!row || typeof(row) == 'undefined'){
8477             return;
8478         }
8479         
8480         var cellIndex = cell.dom.cellIndex;
8481         var rowIndex = this.getRowIndex(row);
8482         
8483         if(this.cellSelection){
8484             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8485         }
8486         
8487         if(this.rowSelection){
8488             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8489         }
8490     },
8491     
8492     sort : function(e,el)
8493     {
8494         var col = Roo.get(el);
8495         
8496         if(!col.hasClass('sortable')){
8497             return;
8498         }
8499         
8500         var sort = col.attr('sort');
8501         var dir = 'ASC';
8502         
8503         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8504             dir = 'DESC';
8505         }
8506         
8507         this.store.sortInfo = {field : sort, direction : dir};
8508         
8509         if (this.footer) {
8510             Roo.log("calling footer first");
8511             this.footer.onClick('first');
8512         } else {
8513         
8514             this.store.load({ params : { start : 0 } });
8515         }
8516     },
8517     
8518     renderHeader : function()
8519     {
8520         var header = {
8521             tag: 'thead',
8522             cn : []
8523         };
8524         
8525         var cm = this.cm;
8526         this.totalWidth = 0;
8527         
8528         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8529             
8530             var config = cm.config[i];
8531             
8532             var c = {
8533                 tag: 'th',
8534                 cls : 'x-hcol-' + i,
8535                 style : '',
8536                 html: cm.getColumnHeader(i)
8537             };
8538             
8539             var hh = '';
8540             
8541             if(typeof(config.sortable) != 'undefined' && config.sortable){
8542                 c.cls = 'sortable';
8543                 c.html = '<i class="fa"></i>' + c.html;
8544             }
8545             
8546             // could use BS4 hidden-..-down 
8547             
8548             if(typeof(config.lgHeader) != 'undefined'){
8549                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8550             }
8551             
8552             if(typeof(config.mdHeader) != 'undefined'){
8553                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8554             }
8555             
8556             if(typeof(config.smHeader) != 'undefined'){
8557                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8558             }
8559             
8560             if(typeof(config.xsHeader) != 'undefined'){
8561                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8562             }
8563             
8564             if(hh.length){
8565                 c.html = hh;
8566             }
8567             
8568             if(typeof(config.tooltip) != 'undefined'){
8569                 c.tooltip = config.tooltip;
8570             }
8571             
8572             if(typeof(config.colspan) != 'undefined'){
8573                 c.colspan = config.colspan;
8574             }
8575             
8576             if(typeof(config.hidden) != 'undefined' && config.hidden){
8577                 c.style += ' display:none;';
8578             }
8579             
8580             if(typeof(config.dataIndex) != 'undefined'){
8581                 c.sort = config.dataIndex;
8582             }
8583             
8584            
8585             
8586             if(typeof(config.align) != 'undefined' && config.align.length){
8587                 c.style += ' text-align:' + config.align + ';';
8588             }
8589             
8590             if(typeof(config.width) != 'undefined'){
8591                 c.style += ' width:' + config.width + 'px;';
8592                 this.totalWidth += config.width;
8593             } else {
8594                 this.totalWidth += 100; // assume minimum of 100 per column?
8595             }
8596             
8597             if(typeof(config.cls) != 'undefined'){
8598                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8599             }
8600             
8601             ['xs','sm','md','lg'].map(function(size){
8602                 
8603                 if(typeof(config[size]) == 'undefined'){
8604                     return;
8605                 }
8606                  
8607                 if (!config[size]) { // 0 = hidden
8608                     // BS 4 '0' is treated as hide that column and below.
8609                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8610                     return;
8611                 }
8612                 
8613                 c.cls += ' col-' + size + '-' + config[size] + (
8614                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8615                 );
8616                 
8617                 
8618             });
8619             
8620             header.cn.push(c)
8621         }
8622         
8623         return header;
8624     },
8625     
8626     renderBody : function()
8627     {
8628         var body = {
8629             tag: 'tbody',
8630             cn : [
8631                 {
8632                     tag: 'tr',
8633                     cn : [
8634                         {
8635                             tag : 'td',
8636                             colspan :  this.cm.getColumnCount()
8637                         }
8638                     ]
8639                 }
8640             ]
8641         };
8642         
8643         return body;
8644     },
8645     
8646     renderFooter : function()
8647     {
8648         var footer = {
8649             tag: 'tfoot',
8650             cn : [
8651                 {
8652                     tag: 'tr',
8653                     cn : [
8654                         {
8655                             tag : 'td',
8656                             colspan :  this.cm.getColumnCount()
8657                         }
8658                     ]
8659                 }
8660             ]
8661         };
8662         
8663         return footer;
8664     },
8665     
8666     
8667     
8668     onLoad : function()
8669     {
8670 //        Roo.log('ds onload');
8671         this.clear();
8672         
8673         var _this = this;
8674         var cm = this.cm;
8675         var ds = this.store;
8676         
8677         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8678             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8679             if (_this.store.sortInfo) {
8680                     
8681                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8682                     e.select('i', true).addClass(['fa-arrow-up']);
8683                 }
8684                 
8685                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8686                     e.select('i', true).addClass(['fa-arrow-down']);
8687                 }
8688             }
8689         });
8690         
8691         var tbody =  this.mainBody;
8692               
8693         if(ds.getCount() > 0){
8694             ds.data.each(function(d,rowIndex){
8695                 var row =  this.renderRow(cm, ds, rowIndex);
8696                 
8697                 tbody.createChild(row);
8698                 
8699                 var _this = this;
8700                 
8701                 if(row.cellObjects.length){
8702                     Roo.each(row.cellObjects, function(r){
8703                         _this.renderCellObject(r);
8704                     })
8705                 }
8706                 
8707             }, this);
8708         }
8709         
8710         var tfoot = this.el.select('tfoot', true).first();
8711         
8712         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8713             
8714             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8715             
8716             var total = this.ds.getTotalCount();
8717             
8718             if(this.footer.pageSize < total){
8719                 this.mainFoot.show();
8720             }
8721         }
8722         
8723         Roo.each(this.el.select('tbody td', true).elements, function(e){
8724             e.on('mouseover', _this.onMouseover, _this);
8725         });
8726         
8727         Roo.each(this.el.select('tbody td', true).elements, function(e){
8728             e.on('mouseout', _this.onMouseout, _this);
8729         });
8730         this.fireEvent('rowsrendered', this);
8731         
8732         this.autoSize();
8733     },
8734     
8735     
8736     onUpdate : function(ds,record)
8737     {
8738         this.refreshRow(record);
8739         this.autoSize();
8740     },
8741     
8742     onRemove : function(ds, record, index, isUpdate){
8743         if(isUpdate !== true){
8744             this.fireEvent("beforerowremoved", this, index, record);
8745         }
8746         var bt = this.mainBody.dom;
8747         
8748         var rows = this.el.select('tbody > tr', true).elements;
8749         
8750         if(typeof(rows[index]) != 'undefined'){
8751             bt.removeChild(rows[index].dom);
8752         }
8753         
8754 //        if(bt.rows[index]){
8755 //            bt.removeChild(bt.rows[index]);
8756 //        }
8757         
8758         if(isUpdate !== true){
8759             //this.stripeRows(index);
8760             //this.syncRowHeights(index, index);
8761             //this.layout();
8762             this.fireEvent("rowremoved", this, index, record);
8763         }
8764     },
8765     
8766     onAdd : function(ds, records, rowIndex)
8767     {
8768         //Roo.log('on Add called');
8769         // - note this does not handle multiple adding very well..
8770         var bt = this.mainBody.dom;
8771         for (var i =0 ; i < records.length;i++) {
8772             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8773             //Roo.log(records[i]);
8774             //Roo.log(this.store.getAt(rowIndex+i));
8775             this.insertRow(this.store, rowIndex + i, false);
8776             return;
8777         }
8778         
8779     },
8780     
8781     
8782     refreshRow : function(record){
8783         var ds = this.store, index;
8784         if(typeof record == 'number'){
8785             index = record;
8786             record = ds.getAt(index);
8787         }else{
8788             index = ds.indexOf(record);
8789             if (index < 0) {
8790                 return; // should not happen - but seems to 
8791             }
8792         }
8793         this.insertRow(ds, index, true);
8794         this.autoSize();
8795         this.onRemove(ds, record, index+1, true);
8796         this.autoSize();
8797         //this.syncRowHeights(index, index);
8798         //this.layout();
8799         this.fireEvent("rowupdated", this, index, record);
8800     },
8801     
8802     insertRow : function(dm, rowIndex, isUpdate){
8803         
8804         if(!isUpdate){
8805             this.fireEvent("beforerowsinserted", this, rowIndex);
8806         }
8807             //var s = this.getScrollState();
8808         var row = this.renderRow(this.cm, this.store, rowIndex);
8809         // insert before rowIndex..
8810         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8811         
8812         var _this = this;
8813                 
8814         if(row.cellObjects.length){
8815             Roo.each(row.cellObjects, function(r){
8816                 _this.renderCellObject(r);
8817             })
8818         }
8819             
8820         if(!isUpdate){
8821             this.fireEvent("rowsinserted", this, rowIndex);
8822             //this.syncRowHeights(firstRow, lastRow);
8823             //this.stripeRows(firstRow);
8824             //this.layout();
8825         }
8826         
8827     },
8828     
8829     
8830     getRowDom : function(rowIndex)
8831     {
8832         var rows = this.el.select('tbody > tr', true).elements;
8833         
8834         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8835         
8836     },
8837     // returns the object tree for a tr..
8838   
8839     
8840     renderRow : function(cm, ds, rowIndex) 
8841     {
8842         var d = ds.getAt(rowIndex);
8843         
8844         var row = {
8845             tag : 'tr',
8846             cls : 'x-row-' + rowIndex,
8847             cn : []
8848         };
8849             
8850         var cellObjects = [];
8851         
8852         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8853             var config = cm.config[i];
8854             
8855             var renderer = cm.getRenderer(i);
8856             var value = '';
8857             var id = false;
8858             
8859             if(typeof(renderer) !== 'undefined'){
8860                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8861             }
8862             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8863             // and are rendered into the cells after the row is rendered - using the id for the element.
8864             
8865             if(typeof(value) === 'object'){
8866                 id = Roo.id();
8867                 cellObjects.push({
8868                     container : id,
8869                     cfg : value 
8870                 })
8871             }
8872             
8873             var rowcfg = {
8874                 record: d,
8875                 rowIndex : rowIndex,
8876                 colIndex : i,
8877                 rowClass : ''
8878             };
8879
8880             this.fireEvent('rowclass', this, rowcfg);
8881             
8882             var td = {
8883                 tag: 'td',
8884                 cls : rowcfg.rowClass + ' x-col-' + i,
8885                 style: '',
8886                 html: (typeof(value) === 'object') ? '' : value
8887             };
8888             
8889             if (id) {
8890                 td.id = id;
8891             }
8892             
8893             if(typeof(config.colspan) != 'undefined'){
8894                 td.colspan = config.colspan;
8895             }
8896             
8897             if(typeof(config.hidden) != 'undefined' && config.hidden){
8898                 td.style += ' display:none;';
8899             }
8900             
8901             if(typeof(config.align) != 'undefined' && config.align.length){
8902                 td.style += ' text-align:' + config.align + ';';
8903             }
8904             if(typeof(config.valign) != 'undefined' && config.valign.length){
8905                 td.style += ' vertical-align:' + config.valign + ';';
8906             }
8907             
8908             if(typeof(config.width) != 'undefined'){
8909                 td.style += ' width:' +  config.width + 'px;';
8910             }
8911             
8912             if(typeof(config.cursor) != 'undefined'){
8913                 td.style += ' cursor:' +  config.cursor + ';';
8914             }
8915             
8916             if(typeof(config.cls) != 'undefined'){
8917                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8918             }
8919             
8920             ['xs','sm','md','lg'].map(function(size){
8921                 
8922                 if(typeof(config[size]) == 'undefined'){
8923                     return;
8924                 }
8925                 
8926                 
8927                   
8928                 if (!config[size]) { // 0 = hidden
8929                     // BS 4 '0' is treated as hide that column and below.
8930                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8931                     return;
8932                 }
8933                 
8934                 td.cls += ' col-' + size + '-' + config[size] + (
8935                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8936                 );
8937                  
8938
8939             });
8940             
8941             row.cn.push(td);
8942            
8943         }
8944         
8945         row.cellObjects = cellObjects;
8946         
8947         return row;
8948           
8949     },
8950     
8951     
8952     
8953     onBeforeLoad : function()
8954     {
8955         
8956     },
8957      /**
8958      * Remove all rows
8959      */
8960     clear : function()
8961     {
8962         this.el.select('tbody', true).first().dom.innerHTML = '';
8963     },
8964     /**
8965      * Show or hide a row.
8966      * @param {Number} rowIndex to show or hide
8967      * @param {Boolean} state hide
8968      */
8969     setRowVisibility : function(rowIndex, state)
8970     {
8971         var bt = this.mainBody.dom;
8972         
8973         var rows = this.el.select('tbody > tr', true).elements;
8974         
8975         if(typeof(rows[rowIndex]) == 'undefined'){
8976             return;
8977         }
8978         rows[rowIndex].dom.style.display = state ? '' : 'none';
8979     },
8980     
8981     
8982     getSelectionModel : function(){
8983         if(!this.selModel){
8984             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8985         }
8986         return this.selModel;
8987     },
8988     /*
8989      * Render the Roo.bootstrap object from renderder
8990      */
8991     renderCellObject : function(r)
8992     {
8993         var _this = this;
8994         
8995         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8996         
8997         var t = r.cfg.render(r.container);
8998         
8999         if(r.cfg.cn){
9000             Roo.each(r.cfg.cn, function(c){
9001                 var child = {
9002                     container: t.getChildContainer(),
9003                     cfg: c
9004                 };
9005                 _this.renderCellObject(child);
9006             })
9007         }
9008     },
9009     
9010     getRowIndex : function(row)
9011     {
9012         var rowIndex = -1;
9013         
9014         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9015             if(el != row){
9016                 return;
9017             }
9018             
9019             rowIndex = index;
9020         });
9021         
9022         return rowIndex;
9023     },
9024      /**
9025      * Returns the grid's underlying element = used by panel.Grid
9026      * @return {Element} The element
9027      */
9028     getGridEl : function(){
9029         return this.el;
9030     },
9031      /**
9032      * Forces a resize - used by panel.Grid
9033      * @return {Element} The element
9034      */
9035     autoSize : function()
9036     {
9037         //var ctr = Roo.get(this.container.dom.parentElement);
9038         var ctr = Roo.get(this.el.dom);
9039         
9040         var thd = this.getGridEl().select('thead',true).first();
9041         var tbd = this.getGridEl().select('tbody', true).first();
9042         var tfd = this.getGridEl().select('tfoot', true).first();
9043         
9044         var cw = ctr.getWidth();
9045         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9046         
9047         if (tbd) {
9048             
9049             tbd.setWidth(ctr.getWidth());
9050             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9051             // this needs fixing for various usage - currently only hydra job advers I think..
9052             //tdb.setHeight(
9053             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9054             //); 
9055             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9056             cw -= barsize;
9057         }
9058         cw = Math.max(cw, this.totalWidth);
9059         this.getGridEl().select('tbody tr',true).setWidth(cw);
9060         
9061         // resize 'expandable coloumn?
9062         
9063         return; // we doe not have a view in this design..
9064         
9065     },
9066     onBodyScroll: function()
9067     {
9068         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9069         if(this.mainHead){
9070             this.mainHead.setStyle({
9071                 'position' : 'relative',
9072                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9073             });
9074         }
9075         
9076         if(this.lazyLoad){
9077             
9078             var scrollHeight = this.mainBody.dom.scrollHeight;
9079             
9080             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9081             
9082             var height = this.mainBody.getHeight();
9083             
9084             if(scrollHeight - height == scrollTop) {
9085                 
9086                 var total = this.ds.getTotalCount();
9087                 
9088                 if(this.footer.cursor + this.footer.pageSize < total){
9089                     
9090                     this.footer.ds.load({
9091                         params : {
9092                             start : this.footer.cursor + this.footer.pageSize,
9093                             limit : this.footer.pageSize
9094                         },
9095                         add : true
9096                     });
9097                 }
9098             }
9099             
9100         }
9101     },
9102     
9103     onHeaderChange : function()
9104     {
9105         var header = this.renderHeader();
9106         var table = this.el.select('table', true).first();
9107         
9108         this.mainHead.remove();
9109         this.mainHead = table.createChild(header, this.mainBody, false);
9110         
9111         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9112             e.on('click', this.sort, this);
9113         }, this);
9114         
9115         
9116     },
9117     
9118     onHiddenChange : function(colModel, colIndex, hidden)
9119     {
9120         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9121         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9122         
9123         this.CSS.updateRule(thSelector, "display", "");
9124         this.CSS.updateRule(tdSelector, "display", "");
9125         
9126         if(hidden){
9127             this.CSS.updateRule(thSelector, "display", "none");
9128             this.CSS.updateRule(tdSelector, "display", "none");
9129         }
9130         
9131         this.onHeaderChange();
9132         this.onLoad();
9133     },
9134     
9135     setColumnWidth: function(col_index, width)
9136     {
9137         // width = "md-2 xs-2..."
9138         if(!this.colModel.config[col_index]) {
9139             return;
9140         }
9141         
9142         var w = width.split(" ");
9143         
9144         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9145         
9146         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9147         
9148         
9149         for(var j = 0; j < w.length; j++) {
9150             
9151             if(!w[j]) {
9152                 continue;
9153             }
9154             
9155             var size_cls = w[j].split("-");
9156             
9157             if(!Number.isInteger(size_cls[1] * 1)) {
9158                 continue;
9159             }
9160             
9161             if(!this.colModel.config[col_index][size_cls[0]]) {
9162                 continue;
9163             }
9164             
9165             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9166                 continue;
9167             }
9168             
9169             h_row[0].classList.replace(
9170                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9171                 "col-"+size_cls[0]+"-"+size_cls[1]
9172             );
9173             
9174             for(var i = 0; i < rows.length; i++) {
9175                 
9176                 var size_cls = w[j].split("-");
9177                 
9178                 if(!Number.isInteger(size_cls[1] * 1)) {
9179                     continue;
9180                 }
9181                 
9182                 if(!this.colModel.config[col_index][size_cls[0]]) {
9183                     continue;
9184                 }
9185                 
9186                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9187                     continue;
9188                 }
9189                 
9190                 rows[i].classList.replace(
9191                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9192                     "col-"+size_cls[0]+"-"+size_cls[1]
9193                 );
9194             }
9195             
9196             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9197         }
9198     }
9199 });
9200
9201  
9202
9203  /*
9204  * - LGPL
9205  *
9206  * table cell
9207  * 
9208  */
9209
9210 /**
9211  * @class Roo.bootstrap.TableCell
9212  * @extends Roo.bootstrap.Component
9213  * Bootstrap TableCell class
9214  * @cfg {String} html cell contain text
9215  * @cfg {String} cls cell class
9216  * @cfg {String} tag cell tag (td|th) default td
9217  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9218  * @cfg {String} align Aligns the content in a cell
9219  * @cfg {String} axis Categorizes cells
9220  * @cfg {String} bgcolor Specifies the background color of a cell
9221  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9222  * @cfg {Number} colspan Specifies the number of columns a cell should span
9223  * @cfg {String} headers Specifies one or more header cells a cell is related to
9224  * @cfg {Number} height Sets the height of a cell
9225  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9226  * @cfg {Number} rowspan Sets the number of rows a cell should span
9227  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9228  * @cfg {String} valign Vertical aligns the content in a cell
9229  * @cfg {Number} width Specifies the width of a cell
9230  * 
9231  * @constructor
9232  * Create a new TableCell
9233  * @param {Object} config The config object
9234  */
9235
9236 Roo.bootstrap.TableCell = function(config){
9237     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9238 };
9239
9240 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9241     
9242     html: false,
9243     cls: false,
9244     tag: false,
9245     abbr: false,
9246     align: false,
9247     axis: false,
9248     bgcolor: false,
9249     charoff: false,
9250     colspan: false,
9251     headers: false,
9252     height: false,
9253     nowrap: false,
9254     rowspan: false,
9255     scope: false,
9256     valign: false,
9257     width: false,
9258     
9259     
9260     getAutoCreate : function(){
9261         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9262         
9263         cfg = {
9264             tag: 'td'
9265         };
9266         
9267         if(this.tag){
9268             cfg.tag = this.tag;
9269         }
9270         
9271         if (this.html) {
9272             cfg.html=this.html
9273         }
9274         if (this.cls) {
9275             cfg.cls=this.cls
9276         }
9277         if (this.abbr) {
9278             cfg.abbr=this.abbr
9279         }
9280         if (this.align) {
9281             cfg.align=this.align
9282         }
9283         if (this.axis) {
9284             cfg.axis=this.axis
9285         }
9286         if (this.bgcolor) {
9287             cfg.bgcolor=this.bgcolor
9288         }
9289         if (this.charoff) {
9290             cfg.charoff=this.charoff
9291         }
9292         if (this.colspan) {
9293             cfg.colspan=this.colspan
9294         }
9295         if (this.headers) {
9296             cfg.headers=this.headers
9297         }
9298         if (this.height) {
9299             cfg.height=this.height
9300         }
9301         if (this.nowrap) {
9302             cfg.nowrap=this.nowrap
9303         }
9304         if (this.rowspan) {
9305             cfg.rowspan=this.rowspan
9306         }
9307         if (this.scope) {
9308             cfg.scope=this.scope
9309         }
9310         if (this.valign) {
9311             cfg.valign=this.valign
9312         }
9313         if (this.width) {
9314             cfg.width=this.width
9315         }
9316         
9317         
9318         return cfg;
9319     }
9320    
9321 });
9322
9323  
9324
9325  /*
9326  * - LGPL
9327  *
9328  * table row
9329  * 
9330  */
9331
9332 /**
9333  * @class Roo.bootstrap.TableRow
9334  * @extends Roo.bootstrap.Component
9335  * Bootstrap TableRow class
9336  * @cfg {String} cls row class
9337  * @cfg {String} align Aligns the content in a table row
9338  * @cfg {String} bgcolor Specifies a background color for a table row
9339  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9340  * @cfg {String} valign Vertical aligns the content in a table row
9341  * 
9342  * @constructor
9343  * Create a new TableRow
9344  * @param {Object} config The config object
9345  */
9346
9347 Roo.bootstrap.TableRow = function(config){
9348     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9349 };
9350
9351 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9352     
9353     cls: false,
9354     align: false,
9355     bgcolor: false,
9356     charoff: false,
9357     valign: false,
9358     
9359     getAutoCreate : function(){
9360         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9361         
9362         cfg = {
9363             tag: 'tr'
9364         };
9365             
9366         if(this.cls){
9367             cfg.cls = this.cls;
9368         }
9369         if(this.align){
9370             cfg.align = this.align;
9371         }
9372         if(this.bgcolor){
9373             cfg.bgcolor = this.bgcolor;
9374         }
9375         if(this.charoff){
9376             cfg.charoff = this.charoff;
9377         }
9378         if(this.valign){
9379             cfg.valign = this.valign;
9380         }
9381         
9382         return cfg;
9383     }
9384    
9385 });
9386
9387  
9388
9389  /*
9390  * - LGPL
9391  *
9392  * table body
9393  * 
9394  */
9395
9396 /**
9397  * @class Roo.bootstrap.TableBody
9398  * @extends Roo.bootstrap.Component
9399  * Bootstrap TableBody class
9400  * @cfg {String} cls element class
9401  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9402  * @cfg {String} align Aligns the content inside the element
9403  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9404  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9405  * 
9406  * @constructor
9407  * Create a new TableBody
9408  * @param {Object} config The config object
9409  */
9410
9411 Roo.bootstrap.TableBody = function(config){
9412     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9413 };
9414
9415 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9416     
9417     cls: false,
9418     tag: false,
9419     align: false,
9420     charoff: false,
9421     valign: false,
9422     
9423     getAutoCreate : function(){
9424         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9425         
9426         cfg = {
9427             tag: 'tbody'
9428         };
9429             
9430         if (this.cls) {
9431             cfg.cls=this.cls
9432         }
9433         if(this.tag){
9434             cfg.tag = this.tag;
9435         }
9436         
9437         if(this.align){
9438             cfg.align = this.align;
9439         }
9440         if(this.charoff){
9441             cfg.charoff = this.charoff;
9442         }
9443         if(this.valign){
9444             cfg.valign = this.valign;
9445         }
9446         
9447         return cfg;
9448     }
9449     
9450     
9451 //    initEvents : function()
9452 //    {
9453 //        
9454 //        if(!this.store){
9455 //            return;
9456 //        }
9457 //        
9458 //        this.store = Roo.factory(this.store, Roo.data);
9459 //        this.store.on('load', this.onLoad, this);
9460 //        
9461 //        this.store.load();
9462 //        
9463 //    },
9464 //    
9465 //    onLoad: function () 
9466 //    {   
9467 //        this.fireEvent('load', this);
9468 //    }
9469 //    
9470 //   
9471 });
9472
9473  
9474
9475  /*
9476  * Based on:
9477  * Ext JS Library 1.1.1
9478  * Copyright(c) 2006-2007, Ext JS, LLC.
9479  *
9480  * Originally Released Under LGPL - original licence link has changed is not relivant.
9481  *
9482  * Fork - LGPL
9483  * <script type="text/javascript">
9484  */
9485
9486 // as we use this in bootstrap.
9487 Roo.namespace('Roo.form');
9488  /**
9489  * @class Roo.form.Action
9490  * Internal Class used to handle form actions
9491  * @constructor
9492  * @param {Roo.form.BasicForm} el The form element or its id
9493  * @param {Object} config Configuration options
9494  */
9495
9496  
9497  
9498 // define the action interface
9499 Roo.form.Action = function(form, options){
9500     this.form = form;
9501     this.options = options || {};
9502 };
9503 /**
9504  * Client Validation Failed
9505  * @const 
9506  */
9507 Roo.form.Action.CLIENT_INVALID = 'client';
9508 /**
9509  * Server Validation Failed
9510  * @const 
9511  */
9512 Roo.form.Action.SERVER_INVALID = 'server';
9513  /**
9514  * Connect to Server Failed
9515  * @const 
9516  */
9517 Roo.form.Action.CONNECT_FAILURE = 'connect';
9518 /**
9519  * Reading Data from Server Failed
9520  * @const 
9521  */
9522 Roo.form.Action.LOAD_FAILURE = 'load';
9523
9524 Roo.form.Action.prototype = {
9525     type : 'default',
9526     failureType : undefined,
9527     response : undefined,
9528     result : undefined,
9529
9530     // interface method
9531     run : function(options){
9532
9533     },
9534
9535     // interface method
9536     success : function(response){
9537
9538     },
9539
9540     // interface method
9541     handleResponse : function(response){
9542
9543     },
9544
9545     // default connection failure
9546     failure : function(response){
9547         
9548         this.response = response;
9549         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9550         this.form.afterAction(this, false);
9551     },
9552
9553     processResponse : function(response){
9554         this.response = response;
9555         if(!response.responseText){
9556             return true;
9557         }
9558         this.result = this.handleResponse(response);
9559         return this.result;
9560     },
9561
9562     // utility functions used internally
9563     getUrl : function(appendParams){
9564         var url = this.options.url || this.form.url || this.form.el.dom.action;
9565         if(appendParams){
9566             var p = this.getParams();
9567             if(p){
9568                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9569             }
9570         }
9571         return url;
9572     },
9573
9574     getMethod : function(){
9575         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9576     },
9577
9578     getParams : function(){
9579         var bp = this.form.baseParams;
9580         var p = this.options.params;
9581         if(p){
9582             if(typeof p == "object"){
9583                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9584             }else if(typeof p == 'string' && bp){
9585                 p += '&' + Roo.urlEncode(bp);
9586             }
9587         }else if(bp){
9588             p = Roo.urlEncode(bp);
9589         }
9590         return p;
9591     },
9592
9593     createCallback : function(){
9594         return {
9595             success: this.success,
9596             failure: this.failure,
9597             scope: this,
9598             timeout: (this.form.timeout*1000),
9599             upload: this.form.fileUpload ? this.success : undefined
9600         };
9601     }
9602 };
9603
9604 Roo.form.Action.Submit = function(form, options){
9605     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9606 };
9607
9608 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9609     type : 'submit',
9610
9611     haveProgress : false,
9612     uploadComplete : false,
9613     
9614     // uploadProgress indicator.
9615     uploadProgress : function()
9616     {
9617         if (!this.form.progressUrl) {
9618             return;
9619         }
9620         
9621         if (!this.haveProgress) {
9622             Roo.MessageBox.progress("Uploading", "Uploading");
9623         }
9624         if (this.uploadComplete) {
9625            Roo.MessageBox.hide();
9626            return;
9627         }
9628         
9629         this.haveProgress = true;
9630    
9631         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9632         
9633         var c = new Roo.data.Connection();
9634         c.request({
9635             url : this.form.progressUrl,
9636             params: {
9637                 id : uid
9638             },
9639             method: 'GET',
9640             success : function(req){
9641                //console.log(data);
9642                 var rdata = false;
9643                 var edata;
9644                 try  {
9645                    rdata = Roo.decode(req.responseText)
9646                 } catch (e) {
9647                     Roo.log("Invalid data from server..");
9648                     Roo.log(edata);
9649                     return;
9650                 }
9651                 if (!rdata || !rdata.success) {
9652                     Roo.log(rdata);
9653                     Roo.MessageBox.alert(Roo.encode(rdata));
9654                     return;
9655                 }
9656                 var data = rdata.data;
9657                 
9658                 if (this.uploadComplete) {
9659                    Roo.MessageBox.hide();
9660                    return;
9661                 }
9662                    
9663                 if (data){
9664                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9665                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9666                     );
9667                 }
9668                 this.uploadProgress.defer(2000,this);
9669             },
9670        
9671             failure: function(data) {
9672                 Roo.log('progress url failed ');
9673                 Roo.log(data);
9674             },
9675             scope : this
9676         });
9677            
9678     },
9679     
9680     
9681     run : function()
9682     {
9683         // run get Values on the form, so it syncs any secondary forms.
9684         this.form.getValues();
9685         
9686         var o = this.options;
9687         var method = this.getMethod();
9688         var isPost = method == 'POST';
9689         if(o.clientValidation === false || this.form.isValid()){
9690             
9691             if (this.form.progressUrl) {
9692                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9693                     (new Date() * 1) + '' + Math.random());
9694                     
9695             } 
9696             
9697             
9698             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9699                 form:this.form.el.dom,
9700                 url:this.getUrl(!isPost),
9701                 method: method,
9702                 params:isPost ? this.getParams() : null,
9703                 isUpload: this.form.fileUpload,
9704                 formData : this.form.formData
9705             }));
9706             
9707             this.uploadProgress();
9708
9709         }else if (o.clientValidation !== false){ // client validation failed
9710             this.failureType = Roo.form.Action.CLIENT_INVALID;
9711             this.form.afterAction(this, false);
9712         }
9713     },
9714
9715     success : function(response)
9716     {
9717         this.uploadComplete= true;
9718         if (this.haveProgress) {
9719             Roo.MessageBox.hide();
9720         }
9721         
9722         
9723         var result = this.processResponse(response);
9724         if(result === true || result.success){
9725             this.form.afterAction(this, true);
9726             return;
9727         }
9728         if(result.errors){
9729             this.form.markInvalid(result.errors);
9730             this.failureType = Roo.form.Action.SERVER_INVALID;
9731         }
9732         this.form.afterAction(this, false);
9733     },
9734     failure : function(response)
9735     {
9736         this.uploadComplete= true;
9737         if (this.haveProgress) {
9738             Roo.MessageBox.hide();
9739         }
9740         
9741         this.response = response;
9742         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9743         this.form.afterAction(this, false);
9744     },
9745     
9746     handleResponse : function(response){
9747         if(this.form.errorReader){
9748             var rs = this.form.errorReader.read(response);
9749             var errors = [];
9750             if(rs.records){
9751                 for(var i = 0, len = rs.records.length; i < len; i++) {
9752                     var r = rs.records[i];
9753                     errors[i] = r.data;
9754                 }
9755             }
9756             if(errors.length < 1){
9757                 errors = null;
9758             }
9759             return {
9760                 success : rs.success,
9761                 errors : errors
9762             };
9763         }
9764         var ret = false;
9765         try {
9766             ret = Roo.decode(response.responseText);
9767         } catch (e) {
9768             ret = {
9769                 success: false,
9770                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9771                 errors : []
9772             };
9773         }
9774         return ret;
9775         
9776     }
9777 });
9778
9779
9780 Roo.form.Action.Load = function(form, options){
9781     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9782     this.reader = this.form.reader;
9783 };
9784
9785 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9786     type : 'load',
9787
9788     run : function(){
9789         
9790         Roo.Ajax.request(Roo.apply(
9791                 this.createCallback(), {
9792                     method:this.getMethod(),
9793                     url:this.getUrl(false),
9794                     params:this.getParams()
9795         }));
9796     },
9797
9798     success : function(response){
9799         
9800         var result = this.processResponse(response);
9801         if(result === true || !result.success || !result.data){
9802             this.failureType = Roo.form.Action.LOAD_FAILURE;
9803             this.form.afterAction(this, false);
9804             return;
9805         }
9806         this.form.clearInvalid();
9807         this.form.setValues(result.data);
9808         this.form.afterAction(this, true);
9809     },
9810
9811     handleResponse : function(response){
9812         if(this.form.reader){
9813             var rs = this.form.reader.read(response);
9814             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9815             return {
9816                 success : rs.success,
9817                 data : data
9818             };
9819         }
9820         return Roo.decode(response.responseText);
9821     }
9822 });
9823
9824 Roo.form.Action.ACTION_TYPES = {
9825     'load' : Roo.form.Action.Load,
9826     'submit' : Roo.form.Action.Submit
9827 };/*
9828  * - LGPL
9829  *
9830  * form
9831  *
9832  */
9833
9834 /**
9835  * @class Roo.bootstrap.Form
9836  * @extends Roo.bootstrap.Component
9837  * Bootstrap Form class
9838  * @cfg {String} method  GET | POST (default POST)
9839  * @cfg {String} labelAlign top | left (default top)
9840  * @cfg {String} align left  | right - for navbars
9841  * @cfg {Boolean} loadMask load mask when submit (default true)
9842
9843  *
9844  * @constructor
9845  * Create a new Form
9846  * @param {Object} config The config object
9847  */
9848
9849
9850 Roo.bootstrap.Form = function(config){
9851     
9852     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9853     
9854     Roo.bootstrap.Form.popover.apply();
9855     
9856     this.addEvents({
9857         /**
9858          * @event clientvalidation
9859          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9860          * @param {Form} this
9861          * @param {Boolean} valid true if the form has passed client-side validation
9862          */
9863         clientvalidation: true,
9864         /**
9865          * @event beforeaction
9866          * Fires before any action is performed. Return false to cancel the action.
9867          * @param {Form} this
9868          * @param {Action} action The action to be performed
9869          */
9870         beforeaction: true,
9871         /**
9872          * @event actionfailed
9873          * Fires when an action fails.
9874          * @param {Form} this
9875          * @param {Action} action The action that failed
9876          */
9877         actionfailed : true,
9878         /**
9879          * @event actioncomplete
9880          * Fires when an action is completed.
9881          * @param {Form} this
9882          * @param {Action} action The action that completed
9883          */
9884         actioncomplete : true
9885     });
9886 };
9887
9888 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9889
9890      /**
9891      * @cfg {String} method
9892      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9893      */
9894     method : 'POST',
9895     /**
9896      * @cfg {String} url
9897      * The URL to use for form actions if one isn't supplied in the action options.
9898      */
9899     /**
9900      * @cfg {Boolean} fileUpload
9901      * Set to true if this form is a file upload.
9902      */
9903
9904     /**
9905      * @cfg {Object} baseParams
9906      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9907      */
9908
9909     /**
9910      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9911      */
9912     timeout: 30,
9913     /**
9914      * @cfg {Sting} align (left|right) for navbar forms
9915      */
9916     align : 'left',
9917
9918     // private
9919     activeAction : null,
9920
9921     /**
9922      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9923      * element by passing it or its id or mask the form itself by passing in true.
9924      * @type Mixed
9925      */
9926     waitMsgTarget : false,
9927
9928     loadMask : true,
9929     
9930     /**
9931      * @cfg {Boolean} errorMask (true|false) default false
9932      */
9933     errorMask : false,
9934     
9935     /**
9936      * @cfg {Number} maskOffset Default 100
9937      */
9938     maskOffset : 100,
9939     
9940     /**
9941      * @cfg {Boolean} maskBody
9942      */
9943     maskBody : false,
9944
9945     getAutoCreate : function(){
9946
9947         var cfg = {
9948             tag: 'form',
9949             method : this.method || 'POST',
9950             id : this.id || Roo.id(),
9951             cls : ''
9952         };
9953         if (this.parent().xtype.match(/^Nav/)) {
9954             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9955
9956         }
9957
9958         if (this.labelAlign == 'left' ) {
9959             cfg.cls += ' form-horizontal';
9960         }
9961
9962
9963         return cfg;
9964     },
9965     initEvents : function()
9966     {
9967         this.el.on('submit', this.onSubmit, this);
9968         // this was added as random key presses on the form where triggering form submit.
9969         this.el.on('keypress', function(e) {
9970             if (e.getCharCode() != 13) {
9971                 return true;
9972             }
9973             // we might need to allow it for textareas.. and some other items.
9974             // check e.getTarget().
9975
9976             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9977                 return true;
9978             }
9979
9980             Roo.log("keypress blocked");
9981
9982             e.preventDefault();
9983             return false;
9984         });
9985         
9986     },
9987     // private
9988     onSubmit : function(e){
9989         e.stopEvent();
9990     },
9991
9992      /**
9993      * Returns true if client-side validation on the form is successful.
9994      * @return Boolean
9995      */
9996     isValid : function(){
9997         var items = this.getItems();
9998         var valid = true;
9999         var target = false;
10000         
10001         items.each(function(f){
10002             
10003             if(f.validate()){
10004                 return;
10005             }
10006             
10007             Roo.log('invalid field: ' + f.name);
10008             
10009             valid = false;
10010
10011             if(!target && f.el.isVisible(true)){
10012                 target = f;
10013             }
10014            
10015         });
10016         
10017         if(this.errorMask && !valid){
10018             Roo.bootstrap.Form.popover.mask(this, target);
10019         }
10020         
10021         return valid;
10022     },
10023     
10024     /**
10025      * Returns true if any fields in this form have changed since their original load.
10026      * @return Boolean
10027      */
10028     isDirty : function(){
10029         var dirty = false;
10030         var items = this.getItems();
10031         items.each(function(f){
10032            if(f.isDirty()){
10033                dirty = true;
10034                return false;
10035            }
10036            return true;
10037         });
10038         return dirty;
10039     },
10040      /**
10041      * Performs a predefined action (submit or load) or custom actions you define on this form.
10042      * @param {String} actionName The name of the action type
10043      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10044      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10045      * accept other config options):
10046      * <pre>
10047 Property          Type             Description
10048 ----------------  ---------------  ----------------------------------------------------------------------------------
10049 url               String           The url for the action (defaults to the form's url)
10050 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10051 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10052 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10053                                    validate the form on the client (defaults to false)
10054      * </pre>
10055      * @return {BasicForm} this
10056      */
10057     doAction : function(action, options){
10058         if(typeof action == 'string'){
10059             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10060         }
10061         if(this.fireEvent('beforeaction', this, action) !== false){
10062             this.beforeAction(action);
10063             action.run.defer(100, action);
10064         }
10065         return this;
10066     },
10067
10068     // private
10069     beforeAction : function(action){
10070         var o = action.options;
10071         
10072         if(this.loadMask){
10073             
10074             if(this.maskBody){
10075                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10076             } else {
10077                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10078             }
10079         }
10080         // not really supported yet.. ??
10081
10082         //if(this.waitMsgTarget === true){
10083         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10084         //}else if(this.waitMsgTarget){
10085         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10086         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10087         //}else {
10088         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10089        // }
10090
10091     },
10092
10093     // private
10094     afterAction : function(action, success){
10095         this.activeAction = null;
10096         var o = action.options;
10097
10098         if(this.loadMask){
10099             
10100             if(this.maskBody){
10101                 Roo.get(document.body).unmask();
10102             } else {
10103                 this.el.unmask();
10104             }
10105         }
10106         
10107         //if(this.waitMsgTarget === true){
10108 //            this.el.unmask();
10109         //}else if(this.waitMsgTarget){
10110         //    this.waitMsgTarget.unmask();
10111         //}else{
10112         //    Roo.MessageBox.updateProgress(1);
10113         //    Roo.MessageBox.hide();
10114        // }
10115         //
10116         if(success){
10117             if(o.reset){
10118                 this.reset();
10119             }
10120             Roo.callback(o.success, o.scope, [this, action]);
10121             this.fireEvent('actioncomplete', this, action);
10122
10123         }else{
10124
10125             // failure condition..
10126             // we have a scenario where updates need confirming.
10127             // eg. if a locking scenario exists..
10128             // we look for { errors : { needs_confirm : true }} in the response.
10129             if (
10130                 (typeof(action.result) != 'undefined')  &&
10131                 (typeof(action.result.errors) != 'undefined')  &&
10132                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10133            ){
10134                 var _t = this;
10135                 Roo.log("not supported yet");
10136                  /*
10137
10138                 Roo.MessageBox.confirm(
10139                     "Change requires confirmation",
10140                     action.result.errorMsg,
10141                     function(r) {
10142                         if (r != 'yes') {
10143                             return;
10144                         }
10145                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10146                     }
10147
10148                 );
10149                 */
10150
10151
10152                 return;
10153             }
10154
10155             Roo.callback(o.failure, o.scope, [this, action]);
10156             // show an error message if no failed handler is set..
10157             if (!this.hasListener('actionfailed')) {
10158                 Roo.log("need to add dialog support");
10159                 /*
10160                 Roo.MessageBox.alert("Error",
10161                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10162                         action.result.errorMsg :
10163                         "Saving Failed, please check your entries or try again"
10164                 );
10165                 */
10166             }
10167
10168             this.fireEvent('actionfailed', this, action);
10169         }
10170
10171     },
10172     /**
10173      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10174      * @param {String} id The value to search for
10175      * @return Field
10176      */
10177     findField : function(id){
10178         var items = this.getItems();
10179         var field = items.get(id);
10180         if(!field){
10181              items.each(function(f){
10182                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10183                     field = f;
10184                     return false;
10185                 }
10186                 return true;
10187             });
10188         }
10189         return field || null;
10190     },
10191      /**
10192      * Mark fields in this form invalid in bulk.
10193      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10194      * @return {BasicForm} this
10195      */
10196     markInvalid : function(errors){
10197         if(errors instanceof Array){
10198             for(var i = 0, len = errors.length; i < len; i++){
10199                 var fieldError = errors[i];
10200                 var f = this.findField(fieldError.id);
10201                 if(f){
10202                     f.markInvalid(fieldError.msg);
10203                 }
10204             }
10205         }else{
10206             var field, id;
10207             for(id in errors){
10208                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10209                     field.markInvalid(errors[id]);
10210                 }
10211             }
10212         }
10213         //Roo.each(this.childForms || [], function (f) {
10214         //    f.markInvalid(errors);
10215         //});
10216
10217         return this;
10218     },
10219
10220     /**
10221      * Set values for fields in this form in bulk.
10222      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10223      * @return {BasicForm} this
10224      */
10225     setValues : function(values){
10226         if(values instanceof Array){ // array of objects
10227             for(var i = 0, len = values.length; i < len; i++){
10228                 var v = values[i];
10229                 var f = this.findField(v.id);
10230                 if(f){
10231                     f.setValue(v.value);
10232                     if(this.trackResetOnLoad){
10233                         f.originalValue = f.getValue();
10234                     }
10235                 }
10236             }
10237         }else{ // object hash
10238             var field, id;
10239             for(id in values){
10240                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10241
10242                     if (field.setFromData &&
10243                         field.valueField &&
10244                         field.displayField &&
10245                         // combos' with local stores can
10246                         // be queried via setValue()
10247                         // to set their value..
10248                         (field.store && !field.store.isLocal)
10249                         ) {
10250                         // it's a combo
10251                         var sd = { };
10252                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10253                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10254                         field.setFromData(sd);
10255
10256                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10257                         
10258                         field.setFromData(values);
10259                         
10260                     } else {
10261                         field.setValue(values[id]);
10262                     }
10263
10264
10265                     if(this.trackResetOnLoad){
10266                         field.originalValue = field.getValue();
10267                     }
10268                 }
10269             }
10270         }
10271
10272         //Roo.each(this.childForms || [], function (f) {
10273         //    f.setValues(values);
10274         //});
10275
10276         return this;
10277     },
10278
10279     /**
10280      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10281      * they are returned as an array.
10282      * @param {Boolean} asString
10283      * @return {Object}
10284      */
10285     getValues : function(asString){
10286         //if (this.childForms) {
10287             // copy values from the child forms
10288         //    Roo.each(this.childForms, function (f) {
10289         //        this.setValues(f.getValues());
10290         //    }, this);
10291         //}
10292
10293
10294
10295         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10296         if(asString === true){
10297             return fs;
10298         }
10299         return Roo.urlDecode(fs);
10300     },
10301
10302     /**
10303      * Returns the fields in this form as an object with key/value pairs.
10304      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10305      * @return {Object}
10306      */
10307     getFieldValues : function(with_hidden)
10308     {
10309         var items = this.getItems();
10310         var ret = {};
10311         items.each(function(f){
10312             
10313             if (!f.getName()) {
10314                 return;
10315             }
10316             
10317             var v = f.getValue();
10318             
10319             if (f.inputType =='radio') {
10320                 if (typeof(ret[f.getName()]) == 'undefined') {
10321                     ret[f.getName()] = ''; // empty..
10322                 }
10323
10324                 if (!f.el.dom.checked) {
10325                     return;
10326
10327                 }
10328                 v = f.el.dom.value;
10329
10330             }
10331             
10332             if(f.xtype == 'MoneyField'){
10333                 ret[f.currencyName] = f.getCurrency();
10334             }
10335
10336             // not sure if this supported any more..
10337             if ((typeof(v) == 'object') && f.getRawValue) {
10338                 v = f.getRawValue() ; // dates..
10339             }
10340             // combo boxes where name != hiddenName...
10341             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10342                 ret[f.name] = f.getRawValue();
10343             }
10344             ret[f.getName()] = v;
10345         });
10346
10347         return ret;
10348     },
10349
10350     /**
10351      * Clears all invalid messages in this form.
10352      * @return {BasicForm} this
10353      */
10354     clearInvalid : function(){
10355         var items = this.getItems();
10356
10357         items.each(function(f){
10358            f.clearInvalid();
10359         });
10360
10361         return this;
10362     },
10363
10364     /**
10365      * Resets this form.
10366      * @return {BasicForm} this
10367      */
10368     reset : function(){
10369         var items = this.getItems();
10370         items.each(function(f){
10371             f.reset();
10372         });
10373
10374         Roo.each(this.childForms || [], function (f) {
10375             f.reset();
10376         });
10377
10378
10379         return this;
10380     },
10381     
10382     getItems : function()
10383     {
10384         var r=new Roo.util.MixedCollection(false, function(o){
10385             return o.id || (o.id = Roo.id());
10386         });
10387         var iter = function(el) {
10388             if (el.inputEl) {
10389                 r.add(el);
10390             }
10391             if (!el.items) {
10392                 return;
10393             }
10394             Roo.each(el.items,function(e) {
10395                 iter(e);
10396             });
10397         };
10398
10399         iter(this);
10400         return r;
10401     },
10402     
10403     hideFields : function(items)
10404     {
10405         Roo.each(items, function(i){
10406             
10407             var f = this.findField(i);
10408             
10409             if(!f){
10410                 return;
10411             }
10412             
10413             f.hide();
10414             
10415         }, this);
10416     },
10417     
10418     showFields : function(items)
10419     {
10420         Roo.each(items, function(i){
10421             
10422             var f = this.findField(i);
10423             
10424             if(!f){
10425                 return;
10426             }
10427             
10428             f.show();
10429             
10430         }, this);
10431     }
10432
10433 });
10434
10435 Roo.apply(Roo.bootstrap.Form, {
10436     
10437     popover : {
10438         
10439         padding : 5,
10440         
10441         isApplied : false,
10442         
10443         isMasked : false,
10444         
10445         form : false,
10446         
10447         target : false,
10448         
10449         toolTip : false,
10450         
10451         intervalID : false,
10452         
10453         maskEl : false,
10454         
10455         apply : function()
10456         {
10457             if(this.isApplied){
10458                 return;
10459             }
10460             
10461             this.maskEl = {
10462                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10463                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10464                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10465                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10466             };
10467             
10468             this.maskEl.top.enableDisplayMode("block");
10469             this.maskEl.left.enableDisplayMode("block");
10470             this.maskEl.bottom.enableDisplayMode("block");
10471             this.maskEl.right.enableDisplayMode("block");
10472             
10473             this.toolTip = new Roo.bootstrap.Tooltip({
10474                 cls : 'roo-form-error-popover',
10475                 alignment : {
10476                     'left' : ['r-l', [-2,0], 'right'],
10477                     'right' : ['l-r', [2,0], 'left'],
10478                     'bottom' : ['tl-bl', [0,2], 'top'],
10479                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10480                 }
10481             });
10482             
10483             this.toolTip.render(Roo.get(document.body));
10484
10485             this.toolTip.el.enableDisplayMode("block");
10486             
10487             Roo.get(document.body).on('click', function(){
10488                 this.unmask();
10489             }, this);
10490             
10491             Roo.get(document.body).on('touchstart', function(){
10492                 this.unmask();
10493             }, this);
10494             
10495             this.isApplied = true
10496         },
10497         
10498         mask : function(form, target)
10499         {
10500             this.form = form;
10501             
10502             this.target = target;
10503             
10504             if(!this.form.errorMask || !target.el){
10505                 return;
10506             }
10507             
10508             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10509             
10510             Roo.log(scrollable);
10511             
10512             var ot = this.target.el.calcOffsetsTo(scrollable);
10513             
10514             var scrollTo = ot[1] - this.form.maskOffset;
10515             
10516             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10517             
10518             scrollable.scrollTo('top', scrollTo);
10519             
10520             var box = this.target.el.getBox();
10521             Roo.log(box);
10522             var zIndex = Roo.bootstrap.Modal.zIndex++;
10523
10524             
10525             this.maskEl.top.setStyle('position', 'absolute');
10526             this.maskEl.top.setStyle('z-index', zIndex);
10527             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10528             this.maskEl.top.setLeft(0);
10529             this.maskEl.top.setTop(0);
10530             this.maskEl.top.show();
10531             
10532             this.maskEl.left.setStyle('position', 'absolute');
10533             this.maskEl.left.setStyle('z-index', zIndex);
10534             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10535             this.maskEl.left.setLeft(0);
10536             this.maskEl.left.setTop(box.y - this.padding);
10537             this.maskEl.left.show();
10538
10539             this.maskEl.bottom.setStyle('position', 'absolute');
10540             this.maskEl.bottom.setStyle('z-index', zIndex);
10541             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10542             this.maskEl.bottom.setLeft(0);
10543             this.maskEl.bottom.setTop(box.bottom + this.padding);
10544             this.maskEl.bottom.show();
10545
10546             this.maskEl.right.setStyle('position', 'absolute');
10547             this.maskEl.right.setStyle('z-index', zIndex);
10548             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10549             this.maskEl.right.setLeft(box.right + this.padding);
10550             this.maskEl.right.setTop(box.y - this.padding);
10551             this.maskEl.right.show();
10552
10553             this.toolTip.bindEl = this.target.el;
10554
10555             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10556
10557             var tip = this.target.blankText;
10558
10559             if(this.target.getValue() !== '' ) {
10560                 
10561                 if (this.target.invalidText.length) {
10562                     tip = this.target.invalidText;
10563                 } else if (this.target.regexText.length){
10564                     tip = this.target.regexText;
10565                 }
10566             }
10567
10568             this.toolTip.show(tip);
10569
10570             this.intervalID = window.setInterval(function() {
10571                 Roo.bootstrap.Form.popover.unmask();
10572             }, 10000);
10573
10574             window.onwheel = function(){ return false;};
10575             
10576             (function(){ this.isMasked = true; }).defer(500, this);
10577             
10578         },
10579         
10580         unmask : function()
10581         {
10582             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10583                 return;
10584             }
10585             
10586             this.maskEl.top.setStyle('position', 'absolute');
10587             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10588             this.maskEl.top.hide();
10589
10590             this.maskEl.left.setStyle('position', 'absolute');
10591             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10592             this.maskEl.left.hide();
10593
10594             this.maskEl.bottom.setStyle('position', 'absolute');
10595             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10596             this.maskEl.bottom.hide();
10597
10598             this.maskEl.right.setStyle('position', 'absolute');
10599             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10600             this.maskEl.right.hide();
10601             
10602             this.toolTip.hide();
10603             
10604             this.toolTip.el.hide();
10605             
10606             window.onwheel = function(){ return true;};
10607             
10608             if(this.intervalID){
10609                 window.clearInterval(this.intervalID);
10610                 this.intervalID = false;
10611             }
10612             
10613             this.isMasked = false;
10614             
10615         }
10616         
10617     }
10618     
10619 });
10620
10621 /*
10622  * Based on:
10623  * Ext JS Library 1.1.1
10624  * Copyright(c) 2006-2007, Ext JS, LLC.
10625  *
10626  * Originally Released Under LGPL - original licence link has changed is not relivant.
10627  *
10628  * Fork - LGPL
10629  * <script type="text/javascript">
10630  */
10631 /**
10632  * @class Roo.form.VTypes
10633  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10634  * @singleton
10635  */
10636 Roo.form.VTypes = function(){
10637     // closure these in so they are only created once.
10638     var alpha = /^[a-zA-Z_]+$/;
10639     var alphanum = /^[a-zA-Z0-9_]+$/;
10640     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10641     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10642
10643     // All these messages and functions are configurable
10644     return {
10645         /**
10646          * The function used to validate email addresses
10647          * @param {String} value The email address
10648          */
10649         'email' : function(v){
10650             return email.test(v);
10651         },
10652         /**
10653          * The error text to display when the email validation function returns false
10654          * @type String
10655          */
10656         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10657         /**
10658          * The keystroke filter mask to be applied on email input
10659          * @type RegExp
10660          */
10661         'emailMask' : /[a-z0-9_\.\-@]/i,
10662
10663         /**
10664          * The function used to validate URLs
10665          * @param {String} value The URL
10666          */
10667         'url' : function(v){
10668             return url.test(v);
10669         },
10670         /**
10671          * The error text to display when the url validation function returns false
10672          * @type String
10673          */
10674         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10675         
10676         /**
10677          * The function used to validate alpha values
10678          * @param {String} value The value
10679          */
10680         'alpha' : function(v){
10681             return alpha.test(v);
10682         },
10683         /**
10684          * The error text to display when the alpha validation function returns false
10685          * @type String
10686          */
10687         'alphaText' : 'This field should only contain letters and _',
10688         /**
10689          * The keystroke filter mask to be applied on alpha input
10690          * @type RegExp
10691          */
10692         'alphaMask' : /[a-z_]/i,
10693
10694         /**
10695          * The function used to validate alphanumeric values
10696          * @param {String} value The value
10697          */
10698         'alphanum' : function(v){
10699             return alphanum.test(v);
10700         },
10701         /**
10702          * The error text to display when the alphanumeric validation function returns false
10703          * @type String
10704          */
10705         'alphanumText' : 'This field should only contain letters, numbers and _',
10706         /**
10707          * The keystroke filter mask to be applied on alphanumeric input
10708          * @type RegExp
10709          */
10710         'alphanumMask' : /[a-z0-9_]/i
10711     };
10712 }();/*
10713  * - LGPL
10714  *
10715  * Input
10716  * 
10717  */
10718
10719 /**
10720  * @class Roo.bootstrap.Input
10721  * @extends Roo.bootstrap.Component
10722  * Bootstrap Input class
10723  * @cfg {Boolean} disabled is it disabled
10724  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10725  * @cfg {String} name name of the input
10726  * @cfg {string} fieldLabel - the label associated
10727  * @cfg {string} placeholder - placeholder to put in text.
10728  * @cfg {string}  before - input group add on before
10729  * @cfg {string} after - input group add on after
10730  * @cfg {string} size - (lg|sm) or leave empty..
10731  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10732  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10733  * @cfg {Number} md colspan out of 12 for computer-sized screens
10734  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10735  * @cfg {string} value default value of the input
10736  * @cfg {Number} labelWidth set the width of label 
10737  * @cfg {Number} labellg set the width of label (1-12)
10738  * @cfg {Number} labelmd set the width of label (1-12)
10739  * @cfg {Number} labelsm set the width of label (1-12)
10740  * @cfg {Number} labelxs set the width of label (1-12)
10741  * @cfg {String} labelAlign (top|left)
10742  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10743  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10744  * @cfg {String} indicatorpos (left|right) default left
10745  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10746  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10747  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10748
10749  * @cfg {String} align (left|center|right) Default left
10750  * @cfg {Boolean} forceFeedback (true|false) Default false
10751  * 
10752  * @constructor
10753  * Create a new Input
10754  * @param {Object} config The config object
10755  */
10756
10757 Roo.bootstrap.Input = function(config){
10758     
10759     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10760     
10761     this.addEvents({
10762         /**
10763          * @event focus
10764          * Fires when this field receives input focus.
10765          * @param {Roo.form.Field} this
10766          */
10767         focus : true,
10768         /**
10769          * @event blur
10770          * Fires when this field loses input focus.
10771          * @param {Roo.form.Field} this
10772          */
10773         blur : true,
10774         /**
10775          * @event specialkey
10776          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10777          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10778          * @param {Roo.form.Field} this
10779          * @param {Roo.EventObject} e The event object
10780          */
10781         specialkey : true,
10782         /**
10783          * @event change
10784          * Fires just before the field blurs if the field value has changed.
10785          * @param {Roo.form.Field} this
10786          * @param {Mixed} newValue The new value
10787          * @param {Mixed} oldValue The original value
10788          */
10789         change : true,
10790         /**
10791          * @event invalid
10792          * Fires after the field has been marked as invalid.
10793          * @param {Roo.form.Field} this
10794          * @param {String} msg The validation message
10795          */
10796         invalid : true,
10797         /**
10798          * @event valid
10799          * Fires after the field has been validated with no errors.
10800          * @param {Roo.form.Field} this
10801          */
10802         valid : true,
10803          /**
10804          * @event keyup
10805          * Fires after the key up
10806          * @param {Roo.form.Field} this
10807          * @param {Roo.EventObject}  e The event Object
10808          */
10809         keyup : true,
10810         /**
10811          * @event paste
10812          * Fires after the user pastes into input
10813          * @param {Roo.form.Field} this
10814          * @param {Roo.EventObject}  e The event Object
10815          */
10816         paste : true
10817     });
10818 };
10819
10820 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10821      /**
10822      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10823       automatic validation (defaults to "keyup").
10824      */
10825     validationEvent : "keyup",
10826      /**
10827      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10828      */
10829     validateOnBlur : true,
10830     /**
10831      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10832      */
10833     validationDelay : 250,
10834      /**
10835      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10836      */
10837     focusClass : "x-form-focus",  // not needed???
10838     
10839        
10840     /**
10841      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10842      */
10843     invalidClass : "has-warning",
10844     
10845     /**
10846      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10847      */
10848     validClass : "has-success",
10849     
10850     /**
10851      * @cfg {Boolean} hasFeedback (true|false) default true
10852      */
10853     hasFeedback : true,
10854     
10855     /**
10856      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10857      */
10858     invalidFeedbackClass : "glyphicon-warning-sign",
10859     
10860     /**
10861      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10862      */
10863     validFeedbackClass : "glyphicon-ok",
10864     
10865     /**
10866      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10867      */
10868     selectOnFocus : false,
10869     
10870      /**
10871      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10872      */
10873     maskRe : null,
10874        /**
10875      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10876      */
10877     vtype : null,
10878     
10879       /**
10880      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10881      */
10882     disableKeyFilter : false,
10883     
10884        /**
10885      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10886      */
10887     disabled : false,
10888      /**
10889      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10890      */
10891     allowBlank : true,
10892     /**
10893      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10894      */
10895     blankText : "Please complete this mandatory field",
10896     
10897      /**
10898      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10899      */
10900     minLength : 0,
10901     /**
10902      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10903      */
10904     maxLength : Number.MAX_VALUE,
10905     /**
10906      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10907      */
10908     minLengthText : "The minimum length for this field is {0}",
10909     /**
10910      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10911      */
10912     maxLengthText : "The maximum length for this field is {0}",
10913   
10914     
10915     /**
10916      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10917      * If available, this function will be called only after the basic validators all return true, and will be passed the
10918      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10919      */
10920     validator : null,
10921     /**
10922      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10923      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10924      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10925      */
10926     regex : null,
10927     /**
10928      * @cfg {String} regexText -- Depricated - use Invalid Text
10929      */
10930     regexText : "",
10931     
10932     /**
10933      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10934      */
10935     invalidText : "",
10936     
10937     
10938     
10939     autocomplete: false,
10940     
10941     
10942     fieldLabel : '',
10943     inputType : 'text',
10944     
10945     name : false,
10946     placeholder: false,
10947     before : false,
10948     after : false,
10949     size : false,
10950     hasFocus : false,
10951     preventMark: false,
10952     isFormField : true,
10953     value : '',
10954     labelWidth : 2,
10955     labelAlign : false,
10956     readOnly : false,
10957     align : false,
10958     formatedValue : false,
10959     forceFeedback : false,
10960     
10961     indicatorpos : 'left',
10962     
10963     labellg : 0,
10964     labelmd : 0,
10965     labelsm : 0,
10966     labelxs : 0,
10967     
10968     capture : '',
10969     accept : '',
10970     
10971     parentLabelAlign : function()
10972     {
10973         var parent = this;
10974         while (parent.parent()) {
10975             parent = parent.parent();
10976             if (typeof(parent.labelAlign) !='undefined') {
10977                 return parent.labelAlign;
10978             }
10979         }
10980         return 'left';
10981         
10982     },
10983     
10984     getAutoCreate : function()
10985     {
10986         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10987         
10988         var id = Roo.id();
10989         
10990         var cfg = {};
10991         
10992         if(this.inputType != 'hidden'){
10993             cfg.cls = 'form-group' //input-group
10994         }
10995         
10996         var input =  {
10997             tag: 'input',
10998             id : id,
10999             type : this.inputType,
11000             value : this.value,
11001             cls : 'form-control',
11002             placeholder : this.placeholder || '',
11003             autocomplete : this.autocomplete || 'new-password'
11004         };
11005         if (this.inputType == 'file') {
11006             input.style = 'overflow:hidden'; // why not in CSS?
11007         }
11008         
11009         if(this.capture.length){
11010             input.capture = this.capture;
11011         }
11012         
11013         if(this.accept.length){
11014             input.accept = this.accept + "/*";
11015         }
11016         
11017         if(this.align){
11018             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11019         }
11020         
11021         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11022             input.maxLength = this.maxLength;
11023         }
11024         
11025         if (this.disabled) {
11026             input.disabled=true;
11027         }
11028         
11029         if (this.readOnly) {
11030             input.readonly=true;
11031         }
11032         
11033         if (this.name) {
11034             input.name = this.name;
11035         }
11036         
11037         if (this.size) {
11038             input.cls += ' input-' + this.size;
11039         }
11040         
11041         var settings=this;
11042         ['xs','sm','md','lg'].map(function(size){
11043             if (settings[size]) {
11044                 cfg.cls += ' col-' + size + '-' + settings[size];
11045             }
11046         });
11047         
11048         var inputblock = input;
11049         
11050         var feedback = {
11051             tag: 'span',
11052             cls: 'glyphicon form-control-feedback'
11053         };
11054             
11055         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11056             
11057             inputblock = {
11058                 cls : 'has-feedback',
11059                 cn :  [
11060                     input,
11061                     feedback
11062                 ] 
11063             };  
11064         }
11065         
11066         if (this.before || this.after) {
11067             
11068             inputblock = {
11069                 cls : 'input-group',
11070                 cn :  [] 
11071             };
11072             
11073             if (this.before && typeof(this.before) == 'string') {
11074                 
11075                 inputblock.cn.push({
11076                     tag :'span',
11077                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11078                     html : this.before
11079                 });
11080             }
11081             if (this.before && typeof(this.before) == 'object') {
11082                 this.before = Roo.factory(this.before);
11083                 
11084                 inputblock.cn.push({
11085                     tag :'span',
11086                     cls : 'roo-input-before input-group-prepend   input-group-' +
11087                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11088                 });
11089             }
11090             
11091             inputblock.cn.push(input);
11092             
11093             if (this.after && typeof(this.after) == 'string') {
11094                 inputblock.cn.push({
11095                     tag :'span',
11096                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11097                     html : this.after
11098                 });
11099             }
11100             if (this.after && typeof(this.after) == 'object') {
11101                 this.after = Roo.factory(this.after);
11102                 
11103                 inputblock.cn.push({
11104                     tag :'span',
11105                     cls : 'roo-input-after input-group-append  input-group-' +
11106                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11107                 });
11108             }
11109             
11110             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11111                 inputblock.cls += ' has-feedback';
11112                 inputblock.cn.push(feedback);
11113             }
11114         };
11115         var indicator = {
11116             tag : 'i',
11117             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11118             tooltip : 'This field is required'
11119         };
11120         if (this.allowBlank ) {
11121             indicator.style = this.allowBlank ? ' display:none' : '';
11122         }
11123         if (align ==='left' && this.fieldLabel.length) {
11124             
11125             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11126             
11127             cfg.cn = [
11128                 indicator,
11129                 {
11130                     tag: 'label',
11131                     'for' :  id,
11132                     cls : 'control-label col-form-label',
11133                     html : this.fieldLabel
11134
11135                 },
11136                 {
11137                     cls : "", 
11138                     cn: [
11139                         inputblock
11140                     ]
11141                 }
11142             ];
11143             
11144             var labelCfg = cfg.cn[1];
11145             var contentCfg = cfg.cn[2];
11146             
11147             if(this.indicatorpos == 'right'){
11148                 cfg.cn = [
11149                     {
11150                         tag: 'label',
11151                         'for' :  id,
11152                         cls : 'control-label col-form-label',
11153                         cn : [
11154                             {
11155                                 tag : 'span',
11156                                 html : this.fieldLabel
11157                             },
11158                             indicator
11159                         ]
11160                     },
11161                     {
11162                         cls : "",
11163                         cn: [
11164                             inputblock
11165                         ]
11166                     }
11167
11168                 ];
11169                 
11170                 labelCfg = cfg.cn[0];
11171                 contentCfg = cfg.cn[1];
11172             
11173             }
11174             
11175             if(this.labelWidth > 12){
11176                 labelCfg.style = "width: " + this.labelWidth + 'px';
11177             }
11178             
11179             if(this.labelWidth < 13 && this.labelmd == 0){
11180                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11181             }
11182             
11183             if(this.labellg > 0){
11184                 labelCfg.cls += ' col-lg-' + this.labellg;
11185                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11186             }
11187             
11188             if(this.labelmd > 0){
11189                 labelCfg.cls += ' col-md-' + this.labelmd;
11190                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11191             }
11192             
11193             if(this.labelsm > 0){
11194                 labelCfg.cls += ' col-sm-' + this.labelsm;
11195                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11196             }
11197             
11198             if(this.labelxs > 0){
11199                 labelCfg.cls += ' col-xs-' + this.labelxs;
11200                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11201             }
11202             
11203             
11204         } else if ( this.fieldLabel.length) {
11205                 
11206             
11207             
11208             cfg.cn = [
11209                 {
11210                     tag : 'i',
11211                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11212                     tooltip : 'This field is required',
11213                     style : this.allowBlank ? ' display:none' : '' 
11214                 },
11215                 {
11216                     tag: 'label',
11217                    //cls : 'input-group-addon',
11218                     html : this.fieldLabel
11219
11220                 },
11221
11222                inputblock
11223
11224            ];
11225            
11226            if(this.indicatorpos == 'right'){
11227        
11228                 cfg.cn = [
11229                     {
11230                         tag: 'label',
11231                        //cls : 'input-group-addon',
11232                         html : this.fieldLabel
11233
11234                     },
11235                     {
11236                         tag : 'i',
11237                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11238                         tooltip : 'This field is required',
11239                         style : this.allowBlank ? ' display:none' : '' 
11240                     },
11241
11242                    inputblock
11243
11244                ];
11245
11246             }
11247
11248         } else {
11249             
11250             cfg.cn = [
11251
11252                     inputblock
11253
11254             ];
11255                 
11256                 
11257         };
11258         
11259         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11260            cfg.cls += ' navbar-form';
11261         }
11262         
11263         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11264             // on BS4 we do this only if not form 
11265             cfg.cls += ' navbar-form';
11266             cfg.tag = 'li';
11267         }
11268         
11269         return cfg;
11270         
11271     },
11272     /**
11273      * return the real input element.
11274      */
11275     inputEl: function ()
11276     {
11277         return this.el.select('input.form-control',true).first();
11278     },
11279     
11280     tooltipEl : function()
11281     {
11282         return this.inputEl();
11283     },
11284     
11285     indicatorEl : function()
11286     {
11287         if (Roo.bootstrap.version == 4) {
11288             return false; // not enabled in v4 yet.
11289         }
11290         
11291         var indicator = this.el.select('i.roo-required-indicator',true).first();
11292         
11293         if(!indicator){
11294             return false;
11295         }
11296         
11297         return indicator;
11298         
11299     },
11300     
11301     setDisabled : function(v)
11302     {
11303         var i  = this.inputEl().dom;
11304         if (!v) {
11305             i.removeAttribute('disabled');
11306             return;
11307             
11308         }
11309         i.setAttribute('disabled','true');
11310     },
11311     initEvents : function()
11312     {
11313           
11314         this.inputEl().on("keydown" , this.fireKey,  this);
11315         this.inputEl().on("focus", this.onFocus,  this);
11316         this.inputEl().on("blur", this.onBlur,  this);
11317         
11318         this.inputEl().relayEvent('keyup', this);
11319         this.inputEl().relayEvent('paste', this);
11320         
11321         this.indicator = this.indicatorEl();
11322         
11323         if(this.indicator){
11324             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11325         }
11326  
11327         // reference to original value for reset
11328         this.originalValue = this.getValue();
11329         //Roo.form.TextField.superclass.initEvents.call(this);
11330         if(this.validationEvent == 'keyup'){
11331             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11332             this.inputEl().on('keyup', this.filterValidation, this);
11333         }
11334         else if(this.validationEvent !== false){
11335             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11336         }
11337         
11338         if(this.selectOnFocus){
11339             this.on("focus", this.preFocus, this);
11340             
11341         }
11342         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11343             this.inputEl().on("keypress", this.filterKeys, this);
11344         } else {
11345             this.inputEl().relayEvent('keypress', this);
11346         }
11347        /* if(this.grow){
11348             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11349             this.el.on("click", this.autoSize,  this);
11350         }
11351         */
11352         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11353             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11354         }
11355         
11356         if (typeof(this.before) == 'object') {
11357             this.before.render(this.el.select('.roo-input-before',true).first());
11358         }
11359         if (typeof(this.after) == 'object') {
11360             this.after.render(this.el.select('.roo-input-after',true).first());
11361         }
11362         
11363         this.inputEl().on('change', this.onChange, this);
11364         
11365     },
11366     filterValidation : function(e){
11367         if(!e.isNavKeyPress()){
11368             this.validationTask.delay(this.validationDelay);
11369         }
11370     },
11371      /**
11372      * Validates the field value
11373      * @return {Boolean} True if the value is valid, else false
11374      */
11375     validate : function(){
11376         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11377         if(this.disabled || this.validateValue(this.getRawValue())){
11378             this.markValid();
11379             return true;
11380         }
11381         
11382         this.markInvalid();
11383         return false;
11384     },
11385     
11386     
11387     /**
11388      * Validates a value according to the field's validation rules and marks the field as invalid
11389      * if the validation fails
11390      * @param {Mixed} value The value to validate
11391      * @return {Boolean} True if the value is valid, else false
11392      */
11393     validateValue : function(value)
11394     {
11395         if(this.getVisibilityEl().hasClass('hidden')){
11396             return true;
11397         }
11398         
11399         if(value.length < 1)  { // if it's blank
11400             if(this.allowBlank){
11401                 return true;
11402             }
11403             return false;
11404         }
11405         
11406         if(value.length < this.minLength){
11407             return false;
11408         }
11409         if(value.length > this.maxLength){
11410             return false;
11411         }
11412         if(this.vtype){
11413             var vt = Roo.form.VTypes;
11414             if(!vt[this.vtype](value, this)){
11415                 return false;
11416             }
11417         }
11418         if(typeof this.validator == "function"){
11419             var msg = this.validator(value);
11420             if(msg !== true){
11421                 return false;
11422             }
11423             if (typeof(msg) == 'string') {
11424                 this.invalidText = msg;
11425             }
11426         }
11427         
11428         if(this.regex && !this.regex.test(value)){
11429             return false;
11430         }
11431         
11432         return true;
11433     },
11434     
11435      // private
11436     fireKey : function(e){
11437         //Roo.log('field ' + e.getKey());
11438         if(e.isNavKeyPress()){
11439             this.fireEvent("specialkey", this, e);
11440         }
11441     },
11442     focus : function (selectText){
11443         if(this.rendered){
11444             this.inputEl().focus();
11445             if(selectText === true){
11446                 this.inputEl().dom.select();
11447             }
11448         }
11449         return this;
11450     } ,
11451     
11452     onFocus : function(){
11453         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11454            // this.el.addClass(this.focusClass);
11455         }
11456         if(!this.hasFocus){
11457             this.hasFocus = true;
11458             this.startValue = this.getValue();
11459             this.fireEvent("focus", this);
11460         }
11461     },
11462     
11463     beforeBlur : Roo.emptyFn,
11464
11465     
11466     // private
11467     onBlur : function(){
11468         this.beforeBlur();
11469         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11470             //this.el.removeClass(this.focusClass);
11471         }
11472         this.hasFocus = false;
11473         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11474             this.validate();
11475         }
11476         var v = this.getValue();
11477         if(String(v) !== String(this.startValue)){
11478             this.fireEvent('change', this, v, this.startValue);
11479         }
11480         this.fireEvent("blur", this);
11481     },
11482     
11483     onChange : function(e)
11484     {
11485         var v = this.getValue();
11486         if(String(v) !== String(this.startValue)){
11487             this.fireEvent('change', this, v, this.startValue);
11488         }
11489         
11490     },
11491     
11492     /**
11493      * Resets the current field value to the originally loaded value and clears any validation messages
11494      */
11495     reset : function(){
11496         this.setValue(this.originalValue);
11497         this.validate();
11498     },
11499      /**
11500      * Returns the name of the field
11501      * @return {Mixed} name The name field
11502      */
11503     getName: function(){
11504         return this.name;
11505     },
11506      /**
11507      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11508      * @return {Mixed} value The field value
11509      */
11510     getValue : function(){
11511         
11512         var v = this.inputEl().getValue();
11513         
11514         return v;
11515     },
11516     /**
11517      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11518      * @return {Mixed} value The field value
11519      */
11520     getRawValue : function(){
11521         var v = this.inputEl().getValue();
11522         
11523         return v;
11524     },
11525     
11526     /**
11527      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11528      * @param {Mixed} value The value to set
11529      */
11530     setRawValue : function(v){
11531         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11532     },
11533     
11534     selectText : function(start, end){
11535         var v = this.getRawValue();
11536         if(v.length > 0){
11537             start = start === undefined ? 0 : start;
11538             end = end === undefined ? v.length : end;
11539             var d = this.inputEl().dom;
11540             if(d.setSelectionRange){
11541                 d.setSelectionRange(start, end);
11542             }else if(d.createTextRange){
11543                 var range = d.createTextRange();
11544                 range.moveStart("character", start);
11545                 range.moveEnd("character", v.length-end);
11546                 range.select();
11547             }
11548         }
11549     },
11550     
11551     /**
11552      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11553      * @param {Mixed} value The value to set
11554      */
11555     setValue : function(v){
11556         this.value = v;
11557         if(this.rendered){
11558             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11559             this.validate();
11560         }
11561     },
11562     
11563     /*
11564     processValue : function(value){
11565         if(this.stripCharsRe){
11566             var newValue = value.replace(this.stripCharsRe, '');
11567             if(newValue !== value){
11568                 this.setRawValue(newValue);
11569                 return newValue;
11570             }
11571         }
11572         return value;
11573     },
11574   */
11575     preFocus : function(){
11576         
11577         if(this.selectOnFocus){
11578             this.inputEl().dom.select();
11579         }
11580     },
11581     filterKeys : function(e){
11582         var k = e.getKey();
11583         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11584             return;
11585         }
11586         var c = e.getCharCode(), cc = String.fromCharCode(c);
11587         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11588             return;
11589         }
11590         if(!this.maskRe.test(cc)){
11591             e.stopEvent();
11592         }
11593     },
11594      /**
11595      * Clear any invalid styles/messages for this field
11596      */
11597     clearInvalid : function(){
11598         
11599         if(!this.el || this.preventMark){ // not rendered
11600             return;
11601         }
11602         
11603         
11604         this.el.removeClass([this.invalidClass, 'is-invalid']);
11605         
11606         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11607             
11608             var feedback = this.el.select('.form-control-feedback', true).first();
11609             
11610             if(feedback){
11611                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11612             }
11613             
11614         }
11615         
11616         if(this.indicator){
11617             this.indicator.removeClass('visible');
11618             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11619         }
11620         
11621         this.fireEvent('valid', this);
11622     },
11623     
11624      /**
11625      * Mark this field as valid
11626      */
11627     markValid : function()
11628     {
11629         if(!this.el  || this.preventMark){ // not rendered...
11630             return;
11631         }
11632         
11633         this.el.removeClass([this.invalidClass, this.validClass]);
11634         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11635
11636         var feedback = this.el.select('.form-control-feedback', true).first();
11637             
11638         if(feedback){
11639             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11640         }
11641         
11642         if(this.indicator){
11643             this.indicator.removeClass('visible');
11644             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11645         }
11646         
11647         if(this.disabled){
11648             return;
11649         }
11650         
11651            
11652         if(this.allowBlank && !this.getRawValue().length){
11653             return;
11654         }
11655         if (Roo.bootstrap.version == 3) {
11656             this.el.addClass(this.validClass);
11657         } else {
11658             this.inputEl().addClass('is-valid');
11659         }
11660
11661         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11662             
11663             var feedback = this.el.select('.form-control-feedback', true).first();
11664             
11665             if(feedback){
11666                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11667                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11668             }
11669             
11670         }
11671         
11672         this.fireEvent('valid', this);
11673     },
11674     
11675      /**
11676      * Mark this field as invalid
11677      * @param {String} msg The validation message
11678      */
11679     markInvalid : function(msg)
11680     {
11681         if(!this.el  || this.preventMark){ // not rendered
11682             return;
11683         }
11684         
11685         this.el.removeClass([this.invalidClass, this.validClass]);
11686         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11687         
11688         var feedback = this.el.select('.form-control-feedback', true).first();
11689             
11690         if(feedback){
11691             this.el.select('.form-control-feedback', true).first().removeClass(
11692                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11693         }
11694
11695         if(this.disabled){
11696             return;
11697         }
11698         
11699         if(this.allowBlank && !this.getRawValue().length){
11700             return;
11701         }
11702         
11703         if(this.indicator){
11704             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11705             this.indicator.addClass('visible');
11706         }
11707         if (Roo.bootstrap.version == 3) {
11708             this.el.addClass(this.invalidClass);
11709         } else {
11710             this.inputEl().addClass('is-invalid');
11711         }
11712         
11713         
11714         
11715         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11716             
11717             var feedback = this.el.select('.form-control-feedback', true).first();
11718             
11719             if(feedback){
11720                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11721                 
11722                 if(this.getValue().length || this.forceFeedback){
11723                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11724                 }
11725                 
11726             }
11727             
11728         }
11729         
11730         this.fireEvent('invalid', this, msg);
11731     },
11732     // private
11733     SafariOnKeyDown : function(event)
11734     {
11735         // this is a workaround for a password hang bug on chrome/ webkit.
11736         if (this.inputEl().dom.type != 'password') {
11737             return;
11738         }
11739         
11740         var isSelectAll = false;
11741         
11742         if(this.inputEl().dom.selectionEnd > 0){
11743             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11744         }
11745         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11746             event.preventDefault();
11747             this.setValue('');
11748             return;
11749         }
11750         
11751         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11752             
11753             event.preventDefault();
11754             // this is very hacky as keydown always get's upper case.
11755             //
11756             var cc = String.fromCharCode(event.getCharCode());
11757             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11758             
11759         }
11760     },
11761     adjustWidth : function(tag, w){
11762         tag = tag.toLowerCase();
11763         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11764             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11765                 if(tag == 'input'){
11766                     return w + 2;
11767                 }
11768                 if(tag == 'textarea'){
11769                     return w-2;
11770                 }
11771             }else if(Roo.isOpera){
11772                 if(tag == 'input'){
11773                     return w + 2;
11774                 }
11775                 if(tag == 'textarea'){
11776                     return w-2;
11777                 }
11778             }
11779         }
11780         return w;
11781     },
11782     
11783     setFieldLabel : function(v)
11784     {
11785         if(!this.rendered){
11786             return;
11787         }
11788         
11789         if(this.indicatorEl()){
11790             var ar = this.el.select('label > span',true);
11791             
11792             if (ar.elements.length) {
11793                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11794                 this.fieldLabel = v;
11795                 return;
11796             }
11797             
11798             var br = this.el.select('label',true);
11799             
11800             if(br.elements.length) {
11801                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11802                 this.fieldLabel = v;
11803                 return;
11804             }
11805             
11806             Roo.log('Cannot Found any of label > span || label in input');
11807             return;
11808         }
11809         
11810         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11811         this.fieldLabel = v;
11812         
11813         
11814     }
11815 });
11816
11817  
11818 /*
11819  * - LGPL
11820  *
11821  * Input
11822  * 
11823  */
11824
11825 /**
11826  * @class Roo.bootstrap.TextArea
11827  * @extends Roo.bootstrap.Input
11828  * Bootstrap TextArea class
11829  * @cfg {Number} cols Specifies the visible width of a text area
11830  * @cfg {Number} rows Specifies the visible number of lines in a text area
11831  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11832  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11833  * @cfg {string} html text
11834  * 
11835  * @constructor
11836  * Create a new TextArea
11837  * @param {Object} config The config object
11838  */
11839
11840 Roo.bootstrap.TextArea = function(config){
11841     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11842    
11843 };
11844
11845 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11846      
11847     cols : false,
11848     rows : 5,
11849     readOnly : false,
11850     warp : 'soft',
11851     resize : false,
11852     value: false,
11853     html: false,
11854     
11855     getAutoCreate : function(){
11856         
11857         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11858         
11859         var id = Roo.id();
11860         
11861         var cfg = {};
11862         
11863         if(this.inputType != 'hidden'){
11864             cfg.cls = 'form-group' //input-group
11865         }
11866         
11867         var input =  {
11868             tag: 'textarea',
11869             id : id,
11870             warp : this.warp,
11871             rows : this.rows,
11872             value : this.value || '',
11873             html: this.html || '',
11874             cls : 'form-control',
11875             placeholder : this.placeholder || '' 
11876             
11877         };
11878         
11879         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11880             input.maxLength = this.maxLength;
11881         }
11882         
11883         if(this.resize){
11884             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11885         }
11886         
11887         if(this.cols){
11888             input.cols = this.cols;
11889         }
11890         
11891         if (this.readOnly) {
11892             input.readonly = true;
11893         }
11894         
11895         if (this.name) {
11896             input.name = this.name;
11897         }
11898         
11899         if (this.size) {
11900             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11901         }
11902         
11903         var settings=this;
11904         ['xs','sm','md','lg'].map(function(size){
11905             if (settings[size]) {
11906                 cfg.cls += ' col-' + size + '-' + settings[size];
11907             }
11908         });
11909         
11910         var inputblock = input;
11911         
11912         if(this.hasFeedback && !this.allowBlank){
11913             
11914             var feedback = {
11915                 tag: 'span',
11916                 cls: 'glyphicon form-control-feedback'
11917             };
11918
11919             inputblock = {
11920                 cls : 'has-feedback',
11921                 cn :  [
11922                     input,
11923                     feedback
11924                 ] 
11925             };  
11926         }
11927         
11928         
11929         if (this.before || this.after) {
11930             
11931             inputblock = {
11932                 cls : 'input-group',
11933                 cn :  [] 
11934             };
11935             if (this.before) {
11936                 inputblock.cn.push({
11937                     tag :'span',
11938                     cls : 'input-group-addon',
11939                     html : this.before
11940                 });
11941             }
11942             
11943             inputblock.cn.push(input);
11944             
11945             if(this.hasFeedback && !this.allowBlank){
11946                 inputblock.cls += ' has-feedback';
11947                 inputblock.cn.push(feedback);
11948             }
11949             
11950             if (this.after) {
11951                 inputblock.cn.push({
11952                     tag :'span',
11953                     cls : 'input-group-addon',
11954                     html : this.after
11955                 });
11956             }
11957             
11958         }
11959         
11960         if (align ==='left' && this.fieldLabel.length) {
11961             cfg.cn = [
11962                 {
11963                     tag: 'label',
11964                     'for' :  id,
11965                     cls : 'control-label',
11966                     html : this.fieldLabel
11967                 },
11968                 {
11969                     cls : "",
11970                     cn: [
11971                         inputblock
11972                     ]
11973                 }
11974
11975             ];
11976             
11977             if(this.labelWidth > 12){
11978                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11979             }
11980
11981             if(this.labelWidth < 13 && this.labelmd == 0){
11982                 this.labelmd = this.labelWidth;
11983             }
11984
11985             if(this.labellg > 0){
11986                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11987                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11988             }
11989
11990             if(this.labelmd > 0){
11991                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11992                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11993             }
11994
11995             if(this.labelsm > 0){
11996                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11997                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11998             }
11999
12000             if(this.labelxs > 0){
12001                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12002                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12003             }
12004             
12005         } else if ( this.fieldLabel.length) {
12006             cfg.cn = [
12007
12008                {
12009                    tag: 'label',
12010                    //cls : 'input-group-addon',
12011                    html : this.fieldLabel
12012
12013                },
12014
12015                inputblock
12016
12017            ];
12018
12019         } else {
12020
12021             cfg.cn = [
12022
12023                 inputblock
12024
12025             ];
12026                 
12027         }
12028         
12029         if (this.disabled) {
12030             input.disabled=true;
12031         }
12032         
12033         return cfg;
12034         
12035     },
12036     /**
12037      * return the real textarea element.
12038      */
12039     inputEl: function ()
12040     {
12041         return this.el.select('textarea.form-control',true).first();
12042     },
12043     
12044     /**
12045      * Clear any invalid styles/messages for this field
12046      */
12047     clearInvalid : function()
12048     {
12049         
12050         if(!this.el || this.preventMark){ // not rendered
12051             return;
12052         }
12053         
12054         var label = this.el.select('label', true).first();
12055         var icon = this.el.select('i.fa-star', true).first();
12056         
12057         if(label && icon){
12058             icon.remove();
12059         }
12060         this.el.removeClass( this.validClass);
12061         this.inputEl().removeClass('is-invalid');
12062          
12063         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12064             
12065             var feedback = this.el.select('.form-control-feedback', true).first();
12066             
12067             if(feedback){
12068                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12069             }
12070             
12071         }
12072         
12073         this.fireEvent('valid', this);
12074     },
12075     
12076      /**
12077      * Mark this field as valid
12078      */
12079     markValid : function()
12080     {
12081         if(!this.el  || this.preventMark){ // not rendered
12082             return;
12083         }
12084         
12085         this.el.removeClass([this.invalidClass, this.validClass]);
12086         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12087         
12088         var feedback = this.el.select('.form-control-feedback', true).first();
12089             
12090         if(feedback){
12091             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12092         }
12093
12094         if(this.disabled || this.allowBlank){
12095             return;
12096         }
12097         
12098         var label = this.el.select('label', true).first();
12099         var icon = this.el.select('i.fa-star', true).first();
12100         
12101         if(label && icon){
12102             icon.remove();
12103         }
12104         if (Roo.bootstrap.version == 3) {
12105             this.el.addClass(this.validClass);
12106         } else {
12107             this.inputEl().addClass('is-valid');
12108         }
12109         
12110         
12111         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12112             
12113             var feedback = this.el.select('.form-control-feedback', true).first();
12114             
12115             if(feedback){
12116                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12117                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12118             }
12119             
12120         }
12121         
12122         this.fireEvent('valid', this);
12123     },
12124     
12125      /**
12126      * Mark this field as invalid
12127      * @param {String} msg The validation message
12128      */
12129     markInvalid : function(msg)
12130     {
12131         if(!this.el  || this.preventMark){ // not rendered
12132             return;
12133         }
12134         
12135         this.el.removeClass([this.invalidClass, this.validClass]);
12136         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12137         
12138         var feedback = this.el.select('.form-control-feedback', true).first();
12139             
12140         if(feedback){
12141             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12142         }
12143
12144         if(this.disabled || this.allowBlank){
12145             return;
12146         }
12147         
12148         var label = this.el.select('label', true).first();
12149         var icon = this.el.select('i.fa-star', true).first();
12150         
12151         if(!this.getValue().length && label && !icon){
12152             this.el.createChild({
12153                 tag : 'i',
12154                 cls : 'text-danger fa fa-lg fa-star',
12155                 tooltip : 'This field is required',
12156                 style : 'margin-right:5px;'
12157             }, label, true);
12158         }
12159         
12160         if (Roo.bootstrap.version == 3) {
12161             this.el.addClass(this.invalidClass);
12162         } else {
12163             this.inputEl().addClass('is-invalid');
12164         }
12165         
12166         // fixme ... this may be depricated need to test..
12167         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12168             
12169             var feedback = this.el.select('.form-control-feedback', true).first();
12170             
12171             if(feedback){
12172                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12173                 
12174                 if(this.getValue().length || this.forceFeedback){
12175                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12176                 }
12177                 
12178             }
12179             
12180         }
12181         
12182         this.fireEvent('invalid', this, msg);
12183     }
12184 });
12185
12186  
12187 /*
12188  * - LGPL
12189  *
12190  * trigger field - base class for combo..
12191  * 
12192  */
12193  
12194 /**
12195  * @class Roo.bootstrap.TriggerField
12196  * @extends Roo.bootstrap.Input
12197  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12198  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12199  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12200  * for which you can provide a custom implementation.  For example:
12201  * <pre><code>
12202 var trigger = new Roo.bootstrap.TriggerField();
12203 trigger.onTriggerClick = myTriggerFn;
12204 trigger.applyTo('my-field');
12205 </code></pre>
12206  *
12207  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12208  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12209  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12210  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12211  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12212
12213  * @constructor
12214  * Create a new TriggerField.
12215  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12216  * to the base TextField)
12217  */
12218 Roo.bootstrap.TriggerField = function(config){
12219     this.mimicing = false;
12220     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12221 };
12222
12223 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12224     /**
12225      * @cfg {String} triggerClass A CSS class to apply to the trigger
12226      */
12227      /**
12228      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12229      */
12230     hideTrigger:false,
12231
12232     /**
12233      * @cfg {Boolean} removable (true|false) special filter default false
12234      */
12235     removable : false,
12236     
12237     /** @cfg {Boolean} grow @hide */
12238     /** @cfg {Number} growMin @hide */
12239     /** @cfg {Number} growMax @hide */
12240
12241     /**
12242      * @hide 
12243      * @method
12244      */
12245     autoSize: Roo.emptyFn,
12246     // private
12247     monitorTab : true,
12248     // private
12249     deferHeight : true,
12250
12251     
12252     actionMode : 'wrap',
12253     
12254     caret : false,
12255     
12256     
12257     getAutoCreate : function(){
12258        
12259         var align = this.labelAlign || this.parentLabelAlign();
12260         
12261         var id = Roo.id();
12262         
12263         var cfg = {
12264             cls: 'form-group' //input-group
12265         };
12266         
12267         
12268         var input =  {
12269             tag: 'input',
12270             id : id,
12271             type : this.inputType,
12272             cls : 'form-control',
12273             autocomplete: 'new-password',
12274             placeholder : this.placeholder || '' 
12275             
12276         };
12277         if (this.name) {
12278             input.name = this.name;
12279         }
12280         if (this.size) {
12281             input.cls += ' input-' + this.size;
12282         }
12283         
12284         if (this.disabled) {
12285             input.disabled=true;
12286         }
12287         
12288         var inputblock = input;
12289         
12290         if(this.hasFeedback && !this.allowBlank){
12291             
12292             var feedback = {
12293                 tag: 'span',
12294                 cls: 'glyphicon form-control-feedback'
12295             };
12296             
12297             if(this.removable && !this.editable  ){
12298                 inputblock = {
12299                     cls : 'has-feedback',
12300                     cn :  [
12301                         inputblock,
12302                         {
12303                             tag: 'button',
12304                             html : 'x',
12305                             cls : 'roo-combo-removable-btn close'
12306                         },
12307                         feedback
12308                     ] 
12309                 };
12310             } else {
12311                 inputblock = {
12312                     cls : 'has-feedback',
12313                     cn :  [
12314                         inputblock,
12315                         feedback
12316                     ] 
12317                 };
12318             }
12319
12320         } else {
12321             if(this.removable && !this.editable ){
12322                 inputblock = {
12323                     cls : 'roo-removable',
12324                     cn :  [
12325                         inputblock,
12326                         {
12327                             tag: 'button',
12328                             html : 'x',
12329                             cls : 'roo-combo-removable-btn close'
12330                         }
12331                     ] 
12332                 };
12333             }
12334         }
12335         
12336         if (this.before || this.after) {
12337             
12338             inputblock = {
12339                 cls : 'input-group',
12340                 cn :  [] 
12341             };
12342             if (this.before) {
12343                 inputblock.cn.push({
12344                     tag :'span',
12345                     cls : 'input-group-addon input-group-prepend input-group-text',
12346                     html : this.before
12347                 });
12348             }
12349             
12350             inputblock.cn.push(input);
12351             
12352             if(this.hasFeedback && !this.allowBlank){
12353                 inputblock.cls += ' has-feedback';
12354                 inputblock.cn.push(feedback);
12355             }
12356             
12357             if (this.after) {
12358                 inputblock.cn.push({
12359                     tag :'span',
12360                     cls : 'input-group-addon input-group-append input-group-text',
12361                     html : this.after
12362                 });
12363             }
12364             
12365         };
12366         
12367       
12368         
12369         var ibwrap = inputblock;
12370         
12371         if(this.multiple){
12372             ibwrap = {
12373                 tag: 'ul',
12374                 cls: 'roo-select2-choices',
12375                 cn:[
12376                     {
12377                         tag: 'li',
12378                         cls: 'roo-select2-search-field',
12379                         cn: [
12380
12381                             inputblock
12382                         ]
12383                     }
12384                 ]
12385             };
12386                 
12387         }
12388         
12389         var combobox = {
12390             cls: 'roo-select2-container input-group',
12391             cn: [
12392                  {
12393                     tag: 'input',
12394                     type : 'hidden',
12395                     cls: 'form-hidden-field'
12396                 },
12397                 ibwrap
12398             ]
12399         };
12400         
12401         if(!this.multiple && this.showToggleBtn){
12402             
12403             var caret = {
12404                         tag: 'span',
12405                         cls: 'caret'
12406              };
12407             if (this.caret != false) {
12408                 caret = {
12409                      tag: 'i',
12410                      cls: 'fa fa-' + this.caret
12411                 };
12412                 
12413             }
12414             
12415             combobox.cn.push({
12416                 tag :'span',
12417                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12418                 cn : [
12419                     Roo.bootstrap.version == 3 ? caret : '',
12420                     {
12421                         tag: 'span',
12422                         cls: 'combobox-clear',
12423                         cn  : [
12424                             {
12425                                 tag : 'i',
12426                                 cls: 'icon-remove'
12427                             }
12428                         ]
12429                     }
12430                 ]
12431
12432             })
12433         }
12434         
12435         if(this.multiple){
12436             combobox.cls += ' roo-select2-container-multi';
12437         }
12438          var indicator = {
12439             tag : 'i',
12440             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12441             tooltip : 'This field is required'
12442         };
12443         if (Roo.bootstrap.version == 4) {
12444             indicator = {
12445                 tag : 'i',
12446                 style : 'display:none'
12447             };
12448         }
12449         
12450         
12451         if (align ==='left' && this.fieldLabel.length) {
12452             
12453             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12454
12455             cfg.cn = [
12456                 indicator,
12457                 {
12458                     tag: 'label',
12459                     'for' :  id,
12460                     cls : 'control-label',
12461                     html : this.fieldLabel
12462
12463                 },
12464                 {
12465                     cls : "", 
12466                     cn: [
12467                         combobox
12468                     ]
12469                 }
12470
12471             ];
12472             
12473             var labelCfg = cfg.cn[1];
12474             var contentCfg = cfg.cn[2];
12475             
12476             if(this.indicatorpos == 'right'){
12477                 cfg.cn = [
12478                     {
12479                         tag: 'label',
12480                         'for' :  id,
12481                         cls : 'control-label',
12482                         cn : [
12483                             {
12484                                 tag : 'span',
12485                                 html : this.fieldLabel
12486                             },
12487                             indicator
12488                         ]
12489                     },
12490                     {
12491                         cls : "", 
12492                         cn: [
12493                             combobox
12494                         ]
12495                     }
12496
12497                 ];
12498                 
12499                 labelCfg = cfg.cn[0];
12500                 contentCfg = cfg.cn[1];
12501             }
12502             
12503             if(this.labelWidth > 12){
12504                 labelCfg.style = "width: " + this.labelWidth + 'px';
12505             }
12506             
12507             if(this.labelWidth < 13 && this.labelmd == 0){
12508                 this.labelmd = this.labelWidth;
12509             }
12510             
12511             if(this.labellg > 0){
12512                 labelCfg.cls += ' col-lg-' + this.labellg;
12513                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12514             }
12515             
12516             if(this.labelmd > 0){
12517                 labelCfg.cls += ' col-md-' + this.labelmd;
12518                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12519             }
12520             
12521             if(this.labelsm > 0){
12522                 labelCfg.cls += ' col-sm-' + this.labelsm;
12523                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12524             }
12525             
12526             if(this.labelxs > 0){
12527                 labelCfg.cls += ' col-xs-' + this.labelxs;
12528                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12529             }
12530             
12531         } else if ( this.fieldLabel.length) {
12532 //                Roo.log(" label");
12533             cfg.cn = [
12534                 indicator,
12535                {
12536                    tag: 'label',
12537                    //cls : 'input-group-addon',
12538                    html : this.fieldLabel
12539
12540                },
12541
12542                combobox
12543
12544             ];
12545             
12546             if(this.indicatorpos == 'right'){
12547                 
12548                 cfg.cn = [
12549                     {
12550                        tag: 'label',
12551                        cn : [
12552                            {
12553                                tag : 'span',
12554                                html : this.fieldLabel
12555                            },
12556                            indicator
12557                        ]
12558
12559                     },
12560                     combobox
12561
12562                 ];
12563
12564             }
12565
12566         } else {
12567             
12568 //                Roo.log(" no label && no align");
12569                 cfg = combobox
12570                      
12571                 
12572         }
12573         
12574         var settings=this;
12575         ['xs','sm','md','lg'].map(function(size){
12576             if (settings[size]) {
12577                 cfg.cls += ' col-' + size + '-' + settings[size];
12578             }
12579         });
12580         
12581         return cfg;
12582         
12583     },
12584     
12585     
12586     
12587     // private
12588     onResize : function(w, h){
12589 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12590 //        if(typeof w == 'number'){
12591 //            var x = w - this.trigger.getWidth();
12592 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12593 //            this.trigger.setStyle('left', x+'px');
12594 //        }
12595     },
12596
12597     // private
12598     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12599
12600     // private
12601     getResizeEl : function(){
12602         return this.inputEl();
12603     },
12604
12605     // private
12606     getPositionEl : function(){
12607         return this.inputEl();
12608     },
12609
12610     // private
12611     alignErrorIcon : function(){
12612         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12613     },
12614
12615     // private
12616     initEvents : function(){
12617         
12618         this.createList();
12619         
12620         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12621         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12622         if(!this.multiple && this.showToggleBtn){
12623             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12624             if(this.hideTrigger){
12625                 this.trigger.setDisplayed(false);
12626             }
12627             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12628         }
12629         
12630         if(this.multiple){
12631             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12632         }
12633         
12634         if(this.removable && !this.editable && !this.tickable){
12635             var close = this.closeTriggerEl();
12636             
12637             if(close){
12638                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12639                 close.on('click', this.removeBtnClick, this, close);
12640             }
12641         }
12642         
12643         //this.trigger.addClassOnOver('x-form-trigger-over');
12644         //this.trigger.addClassOnClick('x-form-trigger-click');
12645         
12646         //if(!this.width){
12647         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12648         //}
12649     },
12650     
12651     closeTriggerEl : function()
12652     {
12653         var close = this.el.select('.roo-combo-removable-btn', true).first();
12654         return close ? close : false;
12655     },
12656     
12657     removeBtnClick : function(e, h, el)
12658     {
12659         e.preventDefault();
12660         
12661         if(this.fireEvent("remove", this) !== false){
12662             this.reset();
12663             this.fireEvent("afterremove", this)
12664         }
12665     },
12666     
12667     createList : function()
12668     {
12669         this.list = Roo.get(document.body).createChild({
12670             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12671             cls: 'typeahead typeahead-long dropdown-menu shadow',
12672             style: 'display:none'
12673         });
12674         
12675         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12676         
12677     },
12678
12679     // private
12680     initTrigger : function(){
12681        
12682     },
12683
12684     // private
12685     onDestroy : function(){
12686         if(this.trigger){
12687             this.trigger.removeAllListeners();
12688           //  this.trigger.remove();
12689         }
12690         //if(this.wrap){
12691         //    this.wrap.remove();
12692         //}
12693         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12694     },
12695
12696     // private
12697     onFocus : function(){
12698         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12699         /*
12700         if(!this.mimicing){
12701             this.wrap.addClass('x-trigger-wrap-focus');
12702             this.mimicing = true;
12703             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12704             if(this.monitorTab){
12705                 this.el.on("keydown", this.checkTab, this);
12706             }
12707         }
12708         */
12709     },
12710
12711     // private
12712     checkTab : function(e){
12713         if(e.getKey() == e.TAB){
12714             this.triggerBlur();
12715         }
12716     },
12717
12718     // private
12719     onBlur : function(){
12720         // do nothing
12721     },
12722
12723     // private
12724     mimicBlur : function(e, t){
12725         /*
12726         if(!this.wrap.contains(t) && this.validateBlur()){
12727             this.triggerBlur();
12728         }
12729         */
12730     },
12731
12732     // private
12733     triggerBlur : function(){
12734         this.mimicing = false;
12735         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12736         if(this.monitorTab){
12737             this.el.un("keydown", this.checkTab, this);
12738         }
12739         //this.wrap.removeClass('x-trigger-wrap-focus');
12740         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12741     },
12742
12743     // private
12744     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12745     validateBlur : function(e, t){
12746         return true;
12747     },
12748
12749     // private
12750     onDisable : function(){
12751         this.inputEl().dom.disabled = true;
12752         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12753         //if(this.wrap){
12754         //    this.wrap.addClass('x-item-disabled');
12755         //}
12756     },
12757
12758     // private
12759     onEnable : function(){
12760         this.inputEl().dom.disabled = false;
12761         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12762         //if(this.wrap){
12763         //    this.el.removeClass('x-item-disabled');
12764         //}
12765     },
12766
12767     // private
12768     onShow : function(){
12769         var ae = this.getActionEl();
12770         
12771         if(ae){
12772             ae.dom.style.display = '';
12773             ae.dom.style.visibility = 'visible';
12774         }
12775     },
12776
12777     // private
12778     
12779     onHide : function(){
12780         var ae = this.getActionEl();
12781         ae.dom.style.display = 'none';
12782     },
12783
12784     /**
12785      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12786      * by an implementing function.
12787      * @method
12788      * @param {EventObject} e
12789      */
12790     onTriggerClick : Roo.emptyFn
12791 });
12792  
12793 /*
12794 * Licence: LGPL
12795 */
12796
12797 /**
12798  * @class Roo.bootstrap.CardUploader
12799  * @extends Roo.bootstrap.Button
12800  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12801  * @cfg {Number} errorTimeout default 3000
12802  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12803  * @cfg {Array}  html The button text.
12804
12805  *
12806  * @constructor
12807  * Create a new CardUploader
12808  * @param {Object} config The config object
12809  */
12810
12811 Roo.bootstrap.CardUploader = function(config){
12812     
12813  
12814     
12815     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12816     
12817     
12818     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12819         return r.data.id
12820      });
12821     
12822      this.addEvents({
12823          // raw events
12824         /**
12825          * @event preview
12826          * When a image is clicked on - and needs to display a slideshow or similar..
12827          * @param {Roo.bootstrap.Card} this
12828          * @param {Object} The image information data 
12829          *
12830          */
12831         'preview' : true,
12832          /**
12833          * @event download
12834          * When a the download link is clicked
12835          * @param {Roo.bootstrap.Card} this
12836          * @param {Object} The image information data  contains 
12837          */
12838         'download' : true
12839         
12840     });
12841 };
12842  
12843 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12844     
12845      
12846     errorTimeout : 3000,
12847      
12848     images : false,
12849    
12850     fileCollection : false,
12851     allowBlank : true,
12852     
12853     getAutoCreate : function()
12854     {
12855         
12856         var cfg =  {
12857             cls :'form-group' ,
12858             cn : [
12859                
12860                 {
12861                     tag: 'label',
12862                    //cls : 'input-group-addon',
12863                     html : this.fieldLabel
12864
12865                 },
12866
12867                 {
12868                     tag: 'input',
12869                     type : 'hidden',
12870                     name : this.name,
12871                     value : this.value,
12872                     cls : 'd-none  form-control'
12873                 },
12874                 
12875                 {
12876                     tag: 'input',
12877                     multiple : 'multiple',
12878                     type : 'file',
12879                     cls : 'd-none  roo-card-upload-selector'
12880                 },
12881                 
12882                 {
12883                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12884                 },
12885                 {
12886                     cls : 'card-columns roo-card-uploader-container'
12887                 }
12888
12889             ]
12890         };
12891            
12892          
12893         return cfg;
12894     },
12895     
12896     getChildContainer : function() /// what children are added to.
12897     {
12898         return this.containerEl;
12899     },
12900    
12901     getButtonContainer : function() /// what children are added to.
12902     {
12903         return this.el.select(".roo-card-uploader-button-container").first();
12904     },
12905    
12906     initEvents : function()
12907     {
12908         
12909         Roo.bootstrap.Input.prototype.initEvents.call(this);
12910         
12911         var t = this;
12912         this.addxtype({
12913             xns: Roo.bootstrap,
12914
12915             xtype : 'Button',
12916             container_method : 'getButtonContainer' ,            
12917             html :  this.html, // fix changable?
12918             cls : 'w-100 ',
12919             listeners : {
12920                 'click' : function(btn, e) {
12921                     t.onClick(e);
12922                 }
12923             }
12924         });
12925         
12926         
12927         
12928         
12929         this.urlAPI = (window.createObjectURL && window) || 
12930                                 (window.URL && URL.revokeObjectURL && URL) || 
12931                                 (window.webkitURL && webkitURL);
12932                         
12933          
12934          
12935          
12936         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12937         
12938         this.selectorEl.on('change', this.onFileSelected, this);
12939         if (this.images) {
12940             var t = this;
12941             this.images.forEach(function(img) {
12942                 t.addCard(img)
12943             });
12944             this.images = false;
12945         }
12946         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12947          
12948        
12949     },
12950     
12951    
12952     onClick : function(e)
12953     {
12954         e.preventDefault();
12955          
12956         this.selectorEl.dom.click();
12957          
12958     },
12959     
12960     onFileSelected : function(e)
12961     {
12962         e.preventDefault();
12963         
12964         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12965             return;
12966         }
12967         
12968         Roo.each(this.selectorEl.dom.files, function(file){    
12969             this.addFile(file);
12970         }, this);
12971          
12972     },
12973     
12974       
12975     
12976       
12977     
12978     addFile : function(file)
12979     {
12980            
12981         if(typeof(file) === 'string'){
12982             throw "Add file by name?"; // should not happen
12983             return;
12984         }
12985         
12986         if(!file || !this.urlAPI){
12987             return;
12988         }
12989         
12990         // file;
12991         // file.type;
12992         
12993         var _this = this;
12994         
12995         
12996         var url = _this.urlAPI.createObjectURL( file);
12997            
12998         this.addCard({
12999             id : Roo.bootstrap.CardUploader.ID--,
13000             is_uploaded : false,
13001             src : url,
13002             srcfile : file,
13003             title : file.name,
13004             mimetype : file.type,
13005             preview : false,
13006             is_deleted : 0
13007         });
13008         
13009     },
13010     
13011     /**
13012      * addCard - add an Attachment to the uploader
13013      * @param data - the data about the image to upload
13014      *
13015      * {
13016           id : 123
13017           title : "Title of file",
13018           is_uploaded : false,
13019           src : "http://.....",
13020           srcfile : { the File upload object },
13021           mimetype : file.type,
13022           preview : false,
13023           is_deleted : 0
13024           .. any other data...
13025         }
13026      *
13027      * 
13028     */
13029     
13030     addCard : function (data)
13031     {
13032         // hidden input element?
13033         // if the file is not an image...
13034         //then we need to use something other that and header_image
13035         var t = this;
13036         //   remove.....
13037         var footer = [
13038             {
13039                 xns : Roo.bootstrap,
13040                 xtype : 'CardFooter',
13041                  items: [
13042                     {
13043                         xns : Roo.bootstrap,
13044                         xtype : 'Element',
13045                         cls : 'd-flex',
13046                         items : [
13047                             
13048                             {
13049                                 xns : Roo.bootstrap,
13050                                 xtype : 'Button',
13051                                 html : String.format("<small>{0}</small>", data.title),
13052                                 cls : 'col-10 text-left',
13053                                 size: 'sm',
13054                                 weight: 'link',
13055                                 fa : 'download',
13056                                 listeners : {
13057                                     click : function() {
13058                                      
13059                                         t.fireEvent( "download", t, data );
13060                                     }
13061                                 }
13062                             },
13063                           
13064                             {
13065                                 xns : Roo.bootstrap,
13066                                 xtype : 'Button',
13067                                 style: 'max-height: 28px; ',
13068                                 size : 'sm',
13069                                 weight: 'danger',
13070                                 cls : 'col-2',
13071                                 fa : 'times',
13072                                 listeners : {
13073                                     click : function() {
13074                                         t.removeCard(data.id)
13075                                     }
13076                                 }
13077                             }
13078                         ]
13079                     }
13080                     
13081                 ] 
13082             }
13083             
13084         ];
13085         
13086         var cn = this.addxtype(
13087             {
13088                  
13089                 xns : Roo.bootstrap,
13090                 xtype : 'Card',
13091                 closeable : true,
13092                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13093                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13094                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13095                 data : data,
13096                 html : false,
13097                  
13098                 items : footer,
13099                 initEvents : function() {
13100                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13101                     var card = this;
13102                     this.imgEl = this.el.select('.card-img-top').first();
13103                     if (this.imgEl) {
13104                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13105                         this.imgEl.set({ 'pointer' : 'cursor' });
13106                                   
13107                     }
13108                     this.getCardFooter().addClass('p-1');
13109                     
13110                   
13111                 }
13112                 
13113             }
13114         );
13115         // dont' really need ot update items.
13116         // this.items.push(cn);
13117         this.fileCollection.add(cn);
13118         
13119         if (!data.srcfile) {
13120             this.updateInput();
13121             return;
13122         }
13123             
13124         var _t = this;
13125         var reader = new FileReader();
13126         reader.addEventListener("load", function() {  
13127             data.srcdata =  reader.result;
13128             _t.updateInput();
13129         });
13130         reader.readAsDataURL(data.srcfile);
13131         
13132         
13133         
13134     },
13135     removeCard : function(id)
13136     {
13137         
13138         var card  = this.fileCollection.get(id);
13139         card.data.is_deleted = 1;
13140         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13141         //this.fileCollection.remove(card);
13142         //this.items = this.items.filter(function(e) { return e != card });
13143         // dont' really need ot update items.
13144         card.el.dom.parentNode.removeChild(card.el.dom);
13145         this.updateInput();
13146
13147         
13148     },
13149     reset: function()
13150     {
13151         this.fileCollection.each(function(card) {
13152             if (card.el.dom && card.el.dom.parentNode) {
13153                 card.el.dom.parentNode.removeChild(card.el.dom);
13154             }
13155         });
13156         this.fileCollection.clear();
13157         this.updateInput();
13158     },
13159     
13160     updateInput : function()
13161     {
13162          var data = [];
13163         this.fileCollection.each(function(e) {
13164             data.push(e.data);
13165             
13166         });
13167         this.inputEl().dom.value = JSON.stringify(data);
13168         
13169         
13170         
13171     }
13172     
13173     
13174 });
13175
13176
13177 Roo.bootstrap.CardUploader.ID = -1;/*
13178  * Based on:
13179  * Ext JS Library 1.1.1
13180  * Copyright(c) 2006-2007, Ext JS, LLC.
13181  *
13182  * Originally Released Under LGPL - original licence link has changed is not relivant.
13183  *
13184  * Fork - LGPL
13185  * <script type="text/javascript">
13186  */
13187
13188
13189 /**
13190  * @class Roo.data.SortTypes
13191  * @singleton
13192  * Defines the default sorting (casting?) comparison functions used when sorting data.
13193  */
13194 Roo.data.SortTypes = {
13195     /**
13196      * Default sort that does nothing
13197      * @param {Mixed} s The value being converted
13198      * @return {Mixed} The comparison value
13199      */
13200     none : function(s){
13201         return s;
13202     },
13203     
13204     /**
13205      * The regular expression used to strip tags
13206      * @type {RegExp}
13207      * @property
13208      */
13209     stripTagsRE : /<\/?[^>]+>/gi,
13210     
13211     /**
13212      * Strips all HTML tags to sort on text only
13213      * @param {Mixed} s The value being converted
13214      * @return {String} The comparison value
13215      */
13216     asText : function(s){
13217         return String(s).replace(this.stripTagsRE, "");
13218     },
13219     
13220     /**
13221      * Strips all HTML tags to sort on text only - Case insensitive
13222      * @param {Mixed} s The value being converted
13223      * @return {String} The comparison value
13224      */
13225     asUCText : function(s){
13226         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13227     },
13228     
13229     /**
13230      * Case insensitive string
13231      * @param {Mixed} s The value being converted
13232      * @return {String} The comparison value
13233      */
13234     asUCString : function(s) {
13235         return String(s).toUpperCase();
13236     },
13237     
13238     /**
13239      * Date sorting
13240      * @param {Mixed} s The value being converted
13241      * @return {Number} The comparison value
13242      */
13243     asDate : function(s) {
13244         if(!s){
13245             return 0;
13246         }
13247         if(s instanceof Date){
13248             return s.getTime();
13249         }
13250         return Date.parse(String(s));
13251     },
13252     
13253     /**
13254      * Float sorting
13255      * @param {Mixed} s The value being converted
13256      * @return {Float} The comparison value
13257      */
13258     asFloat : function(s) {
13259         var val = parseFloat(String(s).replace(/,/g, ""));
13260         if(isNaN(val)) {
13261             val = 0;
13262         }
13263         return val;
13264     },
13265     
13266     /**
13267      * Integer sorting
13268      * @param {Mixed} s The value being converted
13269      * @return {Number} The comparison value
13270      */
13271     asInt : function(s) {
13272         var val = parseInt(String(s).replace(/,/g, ""));
13273         if(isNaN(val)) {
13274             val = 0;
13275         }
13276         return val;
13277     }
13278 };/*
13279  * Based on:
13280  * Ext JS Library 1.1.1
13281  * Copyright(c) 2006-2007, Ext JS, LLC.
13282  *
13283  * Originally Released Under LGPL - original licence link has changed is not relivant.
13284  *
13285  * Fork - LGPL
13286  * <script type="text/javascript">
13287  */
13288
13289 /**
13290 * @class Roo.data.Record
13291  * Instances of this class encapsulate both record <em>definition</em> information, and record
13292  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13293  * to access Records cached in an {@link Roo.data.Store} object.<br>
13294  * <p>
13295  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13296  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13297  * objects.<br>
13298  * <p>
13299  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13300  * @constructor
13301  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13302  * {@link #create}. The parameters are the same.
13303  * @param {Array} data An associative Array of data values keyed by the field name.
13304  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13305  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13306  * not specified an integer id is generated.
13307  */
13308 Roo.data.Record = function(data, id){
13309     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13310     this.data = data;
13311 };
13312
13313 /**
13314  * Generate a constructor for a specific record layout.
13315  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13316  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13317  * Each field definition object may contain the following properties: <ul>
13318  * <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,
13319  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13320  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13321  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13322  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13323  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13324  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13325  * this may be omitted.</p></li>
13326  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13327  * <ul><li>auto (Default, implies no conversion)</li>
13328  * <li>string</li>
13329  * <li>int</li>
13330  * <li>float</li>
13331  * <li>boolean</li>
13332  * <li>date</li></ul></p></li>
13333  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13334  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13335  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13336  * by the Reader into an object that will be stored in the Record. It is passed the
13337  * following parameters:<ul>
13338  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13339  * </ul></p></li>
13340  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13341  * </ul>
13342  * <br>usage:<br><pre><code>
13343 var TopicRecord = Roo.data.Record.create(
13344     {name: 'title', mapping: 'topic_title'},
13345     {name: 'author', mapping: 'username'},
13346     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13347     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13348     {name: 'lastPoster', mapping: 'user2'},
13349     {name: 'excerpt', mapping: 'post_text'}
13350 );
13351
13352 var myNewRecord = new TopicRecord({
13353     title: 'Do my job please',
13354     author: 'noobie',
13355     totalPosts: 1,
13356     lastPost: new Date(),
13357     lastPoster: 'Animal',
13358     excerpt: 'No way dude!'
13359 });
13360 myStore.add(myNewRecord);
13361 </code></pre>
13362  * @method create
13363  * @static
13364  */
13365 Roo.data.Record.create = function(o){
13366     var f = function(){
13367         f.superclass.constructor.apply(this, arguments);
13368     };
13369     Roo.extend(f, Roo.data.Record);
13370     var p = f.prototype;
13371     p.fields = new Roo.util.MixedCollection(false, function(field){
13372         return field.name;
13373     });
13374     for(var i = 0, len = o.length; i < len; i++){
13375         p.fields.add(new Roo.data.Field(o[i]));
13376     }
13377     f.getField = function(name){
13378         return p.fields.get(name);  
13379     };
13380     return f;
13381 };
13382
13383 Roo.data.Record.AUTO_ID = 1000;
13384 Roo.data.Record.EDIT = 'edit';
13385 Roo.data.Record.REJECT = 'reject';
13386 Roo.data.Record.COMMIT = 'commit';
13387
13388 Roo.data.Record.prototype = {
13389     /**
13390      * Readonly flag - true if this record has been modified.
13391      * @type Boolean
13392      */
13393     dirty : false,
13394     editing : false,
13395     error: null,
13396     modified: null,
13397
13398     // private
13399     join : function(store){
13400         this.store = store;
13401     },
13402
13403     /**
13404      * Set the named field to the specified value.
13405      * @param {String} name The name of the field to set.
13406      * @param {Object} value The value to set the field to.
13407      */
13408     set : function(name, value){
13409         if(this.data[name] == value){
13410             return;
13411         }
13412         this.dirty = true;
13413         if(!this.modified){
13414             this.modified = {};
13415         }
13416         if(typeof this.modified[name] == 'undefined'){
13417             this.modified[name] = this.data[name];
13418         }
13419         this.data[name] = value;
13420         if(!this.editing && this.store){
13421             this.store.afterEdit(this);
13422         }       
13423     },
13424
13425     /**
13426      * Get the value of the named field.
13427      * @param {String} name The name of the field to get the value of.
13428      * @return {Object} The value of the field.
13429      */
13430     get : function(name){
13431         return this.data[name]; 
13432     },
13433
13434     // private
13435     beginEdit : function(){
13436         this.editing = true;
13437         this.modified = {}; 
13438     },
13439
13440     // private
13441     cancelEdit : function(){
13442         this.editing = false;
13443         delete this.modified;
13444     },
13445
13446     // private
13447     endEdit : function(){
13448         this.editing = false;
13449         if(this.dirty && this.store){
13450             this.store.afterEdit(this);
13451         }
13452     },
13453
13454     /**
13455      * Usually called by the {@link Roo.data.Store} which owns the Record.
13456      * Rejects all changes made to the Record since either creation, or the last commit operation.
13457      * Modified fields are reverted to their original values.
13458      * <p>
13459      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13460      * of reject operations.
13461      */
13462     reject : function(){
13463         var m = this.modified;
13464         for(var n in m){
13465             if(typeof m[n] != "function"){
13466                 this.data[n] = m[n];
13467             }
13468         }
13469         this.dirty = false;
13470         delete this.modified;
13471         this.editing = false;
13472         if(this.store){
13473             this.store.afterReject(this);
13474         }
13475     },
13476
13477     /**
13478      * Usually called by the {@link Roo.data.Store} which owns the Record.
13479      * Commits all changes made to the Record since either creation, or the last commit operation.
13480      * <p>
13481      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13482      * of commit operations.
13483      */
13484     commit : function(){
13485         this.dirty = false;
13486         delete this.modified;
13487         this.editing = false;
13488         if(this.store){
13489             this.store.afterCommit(this);
13490         }
13491     },
13492
13493     // private
13494     hasError : function(){
13495         return this.error != null;
13496     },
13497
13498     // private
13499     clearError : function(){
13500         this.error = null;
13501     },
13502
13503     /**
13504      * Creates a copy of this record.
13505      * @param {String} id (optional) A new record id if you don't want to use this record's id
13506      * @return {Record}
13507      */
13508     copy : function(newId) {
13509         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13510     }
13511 };/*
13512  * Based on:
13513  * Ext JS Library 1.1.1
13514  * Copyright(c) 2006-2007, Ext JS, LLC.
13515  *
13516  * Originally Released Under LGPL - original licence link has changed is not relivant.
13517  *
13518  * Fork - LGPL
13519  * <script type="text/javascript">
13520  */
13521
13522
13523
13524 /**
13525  * @class Roo.data.Store
13526  * @extends Roo.util.Observable
13527  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13528  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13529  * <p>
13530  * 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
13531  * has no knowledge of the format of the data returned by the Proxy.<br>
13532  * <p>
13533  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13534  * instances from the data object. These records are cached and made available through accessor functions.
13535  * @constructor
13536  * Creates a new Store.
13537  * @param {Object} config A config object containing the objects needed for the Store to access data,
13538  * and read the data into Records.
13539  */
13540 Roo.data.Store = function(config){
13541     this.data = new Roo.util.MixedCollection(false);
13542     this.data.getKey = function(o){
13543         return o.id;
13544     };
13545     this.baseParams = {};
13546     // private
13547     this.paramNames = {
13548         "start" : "start",
13549         "limit" : "limit",
13550         "sort" : "sort",
13551         "dir" : "dir",
13552         "multisort" : "_multisort"
13553     };
13554
13555     if(config && config.data){
13556         this.inlineData = config.data;
13557         delete config.data;
13558     }
13559
13560     Roo.apply(this, config);
13561     
13562     if(this.reader){ // reader passed
13563         this.reader = Roo.factory(this.reader, Roo.data);
13564         this.reader.xmodule = this.xmodule || false;
13565         if(!this.recordType){
13566             this.recordType = this.reader.recordType;
13567         }
13568         if(this.reader.onMetaChange){
13569             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13570         }
13571     }
13572
13573     if(this.recordType){
13574         this.fields = this.recordType.prototype.fields;
13575     }
13576     this.modified = [];
13577
13578     this.addEvents({
13579         /**
13580          * @event datachanged
13581          * Fires when the data cache has changed, and a widget which is using this Store
13582          * as a Record cache should refresh its view.
13583          * @param {Store} this
13584          */
13585         datachanged : true,
13586         /**
13587          * @event metachange
13588          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13589          * @param {Store} this
13590          * @param {Object} meta The JSON metadata
13591          */
13592         metachange : true,
13593         /**
13594          * @event add
13595          * Fires when Records have been added to the Store
13596          * @param {Store} this
13597          * @param {Roo.data.Record[]} records The array of Records added
13598          * @param {Number} index The index at which the record(s) were added
13599          */
13600         add : true,
13601         /**
13602          * @event remove
13603          * Fires when a Record has been removed from the Store
13604          * @param {Store} this
13605          * @param {Roo.data.Record} record The Record that was removed
13606          * @param {Number} index The index at which the record was removed
13607          */
13608         remove : true,
13609         /**
13610          * @event update
13611          * Fires when a Record has been updated
13612          * @param {Store} this
13613          * @param {Roo.data.Record} record The Record that was updated
13614          * @param {String} operation The update operation being performed.  Value may be one of:
13615          * <pre><code>
13616  Roo.data.Record.EDIT
13617  Roo.data.Record.REJECT
13618  Roo.data.Record.COMMIT
13619          * </code></pre>
13620          */
13621         update : true,
13622         /**
13623          * @event clear
13624          * Fires when the data cache has been cleared.
13625          * @param {Store} this
13626          */
13627         clear : true,
13628         /**
13629          * @event beforeload
13630          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13631          * the load action will be canceled.
13632          * @param {Store} this
13633          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13634          */
13635         beforeload : true,
13636         /**
13637          * @event beforeloadadd
13638          * Fires after a new set of Records has been loaded.
13639          * @param {Store} this
13640          * @param {Roo.data.Record[]} records The Records that were loaded
13641          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13642          */
13643         beforeloadadd : true,
13644         /**
13645          * @event load
13646          * Fires after a new set of Records has been loaded, before they are added to the store.
13647          * @param {Store} this
13648          * @param {Roo.data.Record[]} records The Records that were loaded
13649          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13650          * @params {Object} return from reader
13651          */
13652         load : true,
13653         /**
13654          * @event loadexception
13655          * Fires if an exception occurs in the Proxy during loading.
13656          * Called with the signature of the Proxy's "loadexception" event.
13657          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13658          * 
13659          * @param {Proxy} 
13660          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13661          * @param {Object} load options 
13662          * @param {Object} jsonData from your request (normally this contains the Exception)
13663          */
13664         loadexception : true
13665     });
13666     
13667     if(this.proxy){
13668         this.proxy = Roo.factory(this.proxy, Roo.data);
13669         this.proxy.xmodule = this.xmodule || false;
13670         this.relayEvents(this.proxy,  ["loadexception"]);
13671     }
13672     this.sortToggle = {};
13673     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13674
13675     Roo.data.Store.superclass.constructor.call(this);
13676
13677     if(this.inlineData){
13678         this.loadData(this.inlineData);
13679         delete this.inlineData;
13680     }
13681 };
13682
13683 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13684      /**
13685     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13686     * without a remote query - used by combo/forms at present.
13687     */
13688     
13689     /**
13690     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13691     */
13692     /**
13693     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13694     */
13695     /**
13696     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13697     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13698     */
13699     /**
13700     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13701     * on any HTTP request
13702     */
13703     /**
13704     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13705     */
13706     /**
13707     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13708     */
13709     multiSort: false,
13710     /**
13711     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13712     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13713     */
13714     remoteSort : false,
13715
13716     /**
13717     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13718      * loaded or when a record is removed. (defaults to false).
13719     */
13720     pruneModifiedRecords : false,
13721
13722     // private
13723     lastOptions : null,
13724
13725     /**
13726      * Add Records to the Store and fires the add event.
13727      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13728      */
13729     add : function(records){
13730         records = [].concat(records);
13731         for(var i = 0, len = records.length; i < len; i++){
13732             records[i].join(this);
13733         }
13734         var index = this.data.length;
13735         this.data.addAll(records);
13736         this.fireEvent("add", this, records, index);
13737     },
13738
13739     /**
13740      * Remove a Record from the Store and fires the remove event.
13741      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13742      */
13743     remove : function(record){
13744         var index = this.data.indexOf(record);
13745         this.data.removeAt(index);
13746  
13747         if(this.pruneModifiedRecords){
13748             this.modified.remove(record);
13749         }
13750         this.fireEvent("remove", this, record, index);
13751     },
13752
13753     /**
13754      * Remove all Records from the Store and fires the clear event.
13755      */
13756     removeAll : function(){
13757         this.data.clear();
13758         if(this.pruneModifiedRecords){
13759             this.modified = [];
13760         }
13761         this.fireEvent("clear", this);
13762     },
13763
13764     /**
13765      * Inserts Records to the Store at the given index and fires the add event.
13766      * @param {Number} index The start index at which to insert the passed Records.
13767      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13768      */
13769     insert : function(index, records){
13770         records = [].concat(records);
13771         for(var i = 0, len = records.length; i < len; i++){
13772             this.data.insert(index, records[i]);
13773             records[i].join(this);
13774         }
13775         this.fireEvent("add", this, records, index);
13776     },
13777
13778     /**
13779      * Get the index within the cache of the passed Record.
13780      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13781      * @return {Number} The index of the passed Record. Returns -1 if not found.
13782      */
13783     indexOf : function(record){
13784         return this.data.indexOf(record);
13785     },
13786
13787     /**
13788      * Get the index within the cache of the Record with the passed id.
13789      * @param {String} id The id of the Record to find.
13790      * @return {Number} The index of the Record. Returns -1 if not found.
13791      */
13792     indexOfId : function(id){
13793         return this.data.indexOfKey(id);
13794     },
13795
13796     /**
13797      * Get the Record with the specified id.
13798      * @param {String} id The id of the Record to find.
13799      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13800      */
13801     getById : function(id){
13802         return this.data.key(id);
13803     },
13804
13805     /**
13806      * Get the Record at the specified index.
13807      * @param {Number} index The index of the Record to find.
13808      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13809      */
13810     getAt : function(index){
13811         return this.data.itemAt(index);
13812     },
13813
13814     /**
13815      * Returns a range of Records between specified indices.
13816      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13817      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13818      * @return {Roo.data.Record[]} An array of Records
13819      */
13820     getRange : function(start, end){
13821         return this.data.getRange(start, end);
13822     },
13823
13824     // private
13825     storeOptions : function(o){
13826         o = Roo.apply({}, o);
13827         delete o.callback;
13828         delete o.scope;
13829         this.lastOptions = o;
13830     },
13831
13832     /**
13833      * Loads the Record cache from the configured Proxy using the configured Reader.
13834      * <p>
13835      * If using remote paging, then the first load call must specify the <em>start</em>
13836      * and <em>limit</em> properties in the options.params property to establish the initial
13837      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13838      * <p>
13839      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13840      * and this call will return before the new data has been loaded. Perform any post-processing
13841      * in a callback function, or in a "load" event handler.</strong>
13842      * <p>
13843      * @param {Object} options An object containing properties which control loading options:<ul>
13844      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13845      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13846      * passed the following arguments:<ul>
13847      * <li>r : Roo.data.Record[]</li>
13848      * <li>options: Options object from the load call</li>
13849      * <li>success: Boolean success indicator</li></ul></li>
13850      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13851      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13852      * </ul>
13853      */
13854     load : function(options){
13855         options = options || {};
13856         if(this.fireEvent("beforeload", this, options) !== false){
13857             this.storeOptions(options);
13858             var p = Roo.apply(options.params || {}, this.baseParams);
13859             // if meta was not loaded from remote source.. try requesting it.
13860             if (!this.reader.metaFromRemote) {
13861                 p._requestMeta = 1;
13862             }
13863             if(this.sortInfo && this.remoteSort){
13864                 var pn = this.paramNames;
13865                 p[pn["sort"]] = this.sortInfo.field;
13866                 p[pn["dir"]] = this.sortInfo.direction;
13867             }
13868             if (this.multiSort) {
13869                 var pn = this.paramNames;
13870                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13871             }
13872             
13873             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13874         }
13875     },
13876
13877     /**
13878      * Reloads the Record cache from the configured Proxy using the configured Reader and
13879      * the options from the last load operation performed.
13880      * @param {Object} options (optional) An object containing properties which may override the options
13881      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13882      * the most recently used options are reused).
13883      */
13884     reload : function(options){
13885         this.load(Roo.applyIf(options||{}, this.lastOptions));
13886     },
13887
13888     // private
13889     // Called as a callback by the Reader during a load operation.
13890     loadRecords : function(o, options, success){
13891         if(!o || success === false){
13892             if(success !== false){
13893                 this.fireEvent("load", this, [], options, o);
13894             }
13895             if(options.callback){
13896                 options.callback.call(options.scope || this, [], options, false);
13897             }
13898             return;
13899         }
13900         // if data returned failure - throw an exception.
13901         if (o.success === false) {
13902             // show a message if no listener is registered.
13903             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13904                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13905             }
13906             // loadmask wil be hooked into this..
13907             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13908             return;
13909         }
13910         var r = o.records, t = o.totalRecords || r.length;
13911         
13912         this.fireEvent("beforeloadadd", this, r, options, o);
13913         
13914         if(!options || options.add !== true){
13915             if(this.pruneModifiedRecords){
13916                 this.modified = [];
13917             }
13918             for(var i = 0, len = r.length; i < len; i++){
13919                 r[i].join(this);
13920             }
13921             if(this.snapshot){
13922                 this.data = this.snapshot;
13923                 delete this.snapshot;
13924             }
13925             this.data.clear();
13926             this.data.addAll(r);
13927             this.totalLength = t;
13928             this.applySort();
13929             this.fireEvent("datachanged", this);
13930         }else{
13931             this.totalLength = Math.max(t, this.data.length+r.length);
13932             this.add(r);
13933         }
13934         
13935         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13936                 
13937             var e = new Roo.data.Record({});
13938
13939             e.set(this.parent.displayField, this.parent.emptyTitle);
13940             e.set(this.parent.valueField, '');
13941
13942             this.insert(0, e);
13943         }
13944             
13945         this.fireEvent("load", this, r, options, o);
13946         if(options.callback){
13947             options.callback.call(options.scope || this, r, options, true);
13948         }
13949     },
13950
13951
13952     /**
13953      * Loads data from a passed data block. A Reader which understands the format of the data
13954      * must have been configured in the constructor.
13955      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13956      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13957      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13958      */
13959     loadData : function(o, append){
13960         var r = this.reader.readRecords(o);
13961         this.loadRecords(r, {add: append}, true);
13962     },
13963     
13964      /**
13965      * using 'cn' the nested child reader read the child array into it's child stores.
13966      * @param {Object} rec The record with a 'children array
13967      */
13968     loadDataFromChildren : function(rec)
13969     {
13970         this.loadData(this.reader.toLoadData(rec));
13971     },
13972     
13973
13974     /**
13975      * Gets the number of cached records.
13976      * <p>
13977      * <em>If using paging, this may not be the total size of the dataset. If the data object
13978      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13979      * the data set size</em>
13980      */
13981     getCount : function(){
13982         return this.data.length || 0;
13983     },
13984
13985     /**
13986      * Gets the total number of records in the dataset as returned by the server.
13987      * <p>
13988      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13989      * the dataset size</em>
13990      */
13991     getTotalCount : function(){
13992         return this.totalLength || 0;
13993     },
13994
13995     /**
13996      * Returns the sort state of the Store as an object with two properties:
13997      * <pre><code>
13998  field {String} The name of the field by which the Records are sorted
13999  direction {String} The sort order, "ASC" or "DESC"
14000      * </code></pre>
14001      */
14002     getSortState : function(){
14003         return this.sortInfo;
14004     },
14005
14006     // private
14007     applySort : function(){
14008         if(this.sortInfo && !this.remoteSort){
14009             var s = this.sortInfo, f = s.field;
14010             var st = this.fields.get(f).sortType;
14011             var fn = function(r1, r2){
14012                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14013                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14014             };
14015             this.data.sort(s.direction, fn);
14016             if(this.snapshot && this.snapshot != this.data){
14017                 this.snapshot.sort(s.direction, fn);
14018             }
14019         }
14020     },
14021
14022     /**
14023      * Sets the default sort column and order to be used by the next load operation.
14024      * @param {String} fieldName The name of the field to sort by.
14025      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14026      */
14027     setDefaultSort : function(field, dir){
14028         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14029     },
14030
14031     /**
14032      * Sort the Records.
14033      * If remote sorting is used, the sort is performed on the server, and the cache is
14034      * reloaded. If local sorting is used, the cache is sorted internally.
14035      * @param {String} fieldName The name of the field to sort by.
14036      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14037      */
14038     sort : function(fieldName, dir){
14039         var f = this.fields.get(fieldName);
14040         if(!dir){
14041             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14042             
14043             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14044                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14045             }else{
14046                 dir = f.sortDir;
14047             }
14048         }
14049         this.sortToggle[f.name] = dir;
14050         this.sortInfo = {field: f.name, direction: dir};
14051         if(!this.remoteSort){
14052             this.applySort();
14053             this.fireEvent("datachanged", this);
14054         }else{
14055             this.load(this.lastOptions);
14056         }
14057     },
14058
14059     /**
14060      * Calls the specified function for each of the Records in the cache.
14061      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14062      * Returning <em>false</em> aborts and exits the iteration.
14063      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14064      */
14065     each : function(fn, scope){
14066         this.data.each(fn, scope);
14067     },
14068
14069     /**
14070      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14071      * (e.g., during paging).
14072      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14073      */
14074     getModifiedRecords : function(){
14075         return this.modified;
14076     },
14077
14078     // private
14079     createFilterFn : function(property, value, anyMatch){
14080         if(!value.exec){ // not a regex
14081             value = String(value);
14082             if(value.length == 0){
14083                 return false;
14084             }
14085             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14086         }
14087         return function(r){
14088             return value.test(r.data[property]);
14089         };
14090     },
14091
14092     /**
14093      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14094      * @param {String} property A field on your records
14095      * @param {Number} start The record index to start at (defaults to 0)
14096      * @param {Number} end The last record index to include (defaults to length - 1)
14097      * @return {Number} The sum
14098      */
14099     sum : function(property, start, end){
14100         var rs = this.data.items, v = 0;
14101         start = start || 0;
14102         end = (end || end === 0) ? end : rs.length-1;
14103
14104         for(var i = start; i <= end; i++){
14105             v += (rs[i].data[property] || 0);
14106         }
14107         return v;
14108     },
14109
14110     /**
14111      * Filter the records by a specified property.
14112      * @param {String} field A field on your records
14113      * @param {String/RegExp} value Either a string that the field
14114      * should start with or a RegExp to test against the field
14115      * @param {Boolean} anyMatch True to match any part not just the beginning
14116      */
14117     filter : function(property, value, anyMatch){
14118         var fn = this.createFilterFn(property, value, anyMatch);
14119         return fn ? this.filterBy(fn) : this.clearFilter();
14120     },
14121
14122     /**
14123      * Filter by a function. The specified function will be called with each
14124      * record in this data source. If the function returns true the record is included,
14125      * otherwise it is filtered.
14126      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14127      * @param {Object} scope (optional) The scope of the function (defaults to this)
14128      */
14129     filterBy : function(fn, scope){
14130         this.snapshot = this.snapshot || this.data;
14131         this.data = this.queryBy(fn, scope||this);
14132         this.fireEvent("datachanged", this);
14133     },
14134
14135     /**
14136      * Query the records by a specified property.
14137      * @param {String} field A field on your records
14138      * @param {String/RegExp} value Either a string that the field
14139      * should start with or a RegExp to test against the field
14140      * @param {Boolean} anyMatch True to match any part not just the beginning
14141      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14142      */
14143     query : function(property, value, anyMatch){
14144         var fn = this.createFilterFn(property, value, anyMatch);
14145         return fn ? this.queryBy(fn) : this.data.clone();
14146     },
14147
14148     /**
14149      * Query by a function. The specified function will be called with each
14150      * record in this data source. If the function returns true the record is included
14151      * in the results.
14152      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14153      * @param {Object} scope (optional) The scope of the function (defaults to this)
14154       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14155      **/
14156     queryBy : function(fn, scope){
14157         var data = this.snapshot || this.data;
14158         return data.filterBy(fn, scope||this);
14159     },
14160
14161     /**
14162      * Collects unique values for a particular dataIndex from this store.
14163      * @param {String} dataIndex The property to collect
14164      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14165      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14166      * @return {Array} An array of the unique values
14167      **/
14168     collect : function(dataIndex, allowNull, bypassFilter){
14169         var d = (bypassFilter === true && this.snapshot) ?
14170                 this.snapshot.items : this.data.items;
14171         var v, sv, r = [], l = {};
14172         for(var i = 0, len = d.length; i < len; i++){
14173             v = d[i].data[dataIndex];
14174             sv = String(v);
14175             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14176                 l[sv] = true;
14177                 r[r.length] = v;
14178             }
14179         }
14180         return r;
14181     },
14182
14183     /**
14184      * Revert to a view of the Record cache with no filtering applied.
14185      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14186      */
14187     clearFilter : function(suppressEvent){
14188         if(this.snapshot && this.snapshot != this.data){
14189             this.data = this.snapshot;
14190             delete this.snapshot;
14191             if(suppressEvent !== true){
14192                 this.fireEvent("datachanged", this);
14193             }
14194         }
14195     },
14196
14197     // private
14198     afterEdit : function(record){
14199         if(this.modified.indexOf(record) == -1){
14200             this.modified.push(record);
14201         }
14202         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14203     },
14204     
14205     // private
14206     afterReject : function(record){
14207         this.modified.remove(record);
14208         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14209     },
14210
14211     // private
14212     afterCommit : function(record){
14213         this.modified.remove(record);
14214         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14215     },
14216
14217     /**
14218      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14219      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14220      */
14221     commitChanges : function(){
14222         var m = this.modified.slice(0);
14223         this.modified = [];
14224         for(var i = 0, len = m.length; i < len; i++){
14225             m[i].commit();
14226         }
14227     },
14228
14229     /**
14230      * Cancel outstanding changes on all changed records.
14231      */
14232     rejectChanges : function(){
14233         var m = this.modified.slice(0);
14234         this.modified = [];
14235         for(var i = 0, len = m.length; i < len; i++){
14236             m[i].reject();
14237         }
14238     },
14239
14240     onMetaChange : function(meta, rtype, o){
14241         this.recordType = rtype;
14242         this.fields = rtype.prototype.fields;
14243         delete this.snapshot;
14244         this.sortInfo = meta.sortInfo || this.sortInfo;
14245         this.modified = [];
14246         this.fireEvent('metachange', this, this.reader.meta);
14247     },
14248     
14249     moveIndex : function(data, type)
14250     {
14251         var index = this.indexOf(data);
14252         
14253         var newIndex = index + type;
14254         
14255         this.remove(data);
14256         
14257         this.insert(newIndex, data);
14258         
14259     }
14260 });/*
14261  * Based on:
14262  * Ext JS Library 1.1.1
14263  * Copyright(c) 2006-2007, Ext JS, LLC.
14264  *
14265  * Originally Released Under LGPL - original licence link has changed is not relivant.
14266  *
14267  * Fork - LGPL
14268  * <script type="text/javascript">
14269  */
14270
14271 /**
14272  * @class Roo.data.SimpleStore
14273  * @extends Roo.data.Store
14274  * Small helper class to make creating Stores from Array data easier.
14275  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14276  * @cfg {Array} fields An array of field definition objects, or field name strings.
14277  * @cfg {Object} an existing reader (eg. copied from another store)
14278  * @cfg {Array} data The multi-dimensional array of data
14279  * @constructor
14280  * @param {Object} config
14281  */
14282 Roo.data.SimpleStore = function(config)
14283 {
14284     Roo.data.SimpleStore.superclass.constructor.call(this, {
14285         isLocal : true,
14286         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14287                 id: config.id
14288             },
14289             Roo.data.Record.create(config.fields)
14290         ),
14291         proxy : new Roo.data.MemoryProxy(config.data)
14292     });
14293     this.load();
14294 };
14295 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14296  * Based on:
14297  * Ext JS Library 1.1.1
14298  * Copyright(c) 2006-2007, Ext JS, LLC.
14299  *
14300  * Originally Released Under LGPL - original licence link has changed is not relivant.
14301  *
14302  * Fork - LGPL
14303  * <script type="text/javascript">
14304  */
14305
14306 /**
14307 /**
14308  * @extends Roo.data.Store
14309  * @class Roo.data.JsonStore
14310  * Small helper class to make creating Stores for JSON data easier. <br/>
14311 <pre><code>
14312 var store = new Roo.data.JsonStore({
14313     url: 'get-images.php',
14314     root: 'images',
14315     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14316 });
14317 </code></pre>
14318  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14319  * JsonReader and HttpProxy (unless inline data is provided).</b>
14320  * @cfg {Array} fields An array of field definition objects, or field name strings.
14321  * @constructor
14322  * @param {Object} config
14323  */
14324 Roo.data.JsonStore = function(c){
14325     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14326         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14327         reader: new Roo.data.JsonReader(c, c.fields)
14328     }));
14329 };
14330 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14331  * Based on:
14332  * Ext JS Library 1.1.1
14333  * Copyright(c) 2006-2007, Ext JS, LLC.
14334  *
14335  * Originally Released Under LGPL - original licence link has changed is not relivant.
14336  *
14337  * Fork - LGPL
14338  * <script type="text/javascript">
14339  */
14340
14341  
14342 Roo.data.Field = function(config){
14343     if(typeof config == "string"){
14344         config = {name: config};
14345     }
14346     Roo.apply(this, config);
14347     
14348     if(!this.type){
14349         this.type = "auto";
14350     }
14351     
14352     var st = Roo.data.SortTypes;
14353     // named sortTypes are supported, here we look them up
14354     if(typeof this.sortType == "string"){
14355         this.sortType = st[this.sortType];
14356     }
14357     
14358     // set default sortType for strings and dates
14359     if(!this.sortType){
14360         switch(this.type){
14361             case "string":
14362                 this.sortType = st.asUCString;
14363                 break;
14364             case "date":
14365                 this.sortType = st.asDate;
14366                 break;
14367             default:
14368                 this.sortType = st.none;
14369         }
14370     }
14371
14372     // define once
14373     var stripRe = /[\$,%]/g;
14374
14375     // prebuilt conversion function for this field, instead of
14376     // switching every time we're reading a value
14377     if(!this.convert){
14378         var cv, dateFormat = this.dateFormat;
14379         switch(this.type){
14380             case "":
14381             case "auto":
14382             case undefined:
14383                 cv = function(v){ return v; };
14384                 break;
14385             case "string":
14386                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14387                 break;
14388             case "int":
14389                 cv = function(v){
14390                     return v !== undefined && v !== null && v !== '' ?
14391                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14392                     };
14393                 break;
14394             case "float":
14395                 cv = function(v){
14396                     return v !== undefined && v !== null && v !== '' ?
14397                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14398                     };
14399                 break;
14400             case "bool":
14401             case "boolean":
14402                 cv = function(v){ return v === true || v === "true" || v == 1; };
14403                 break;
14404             case "date":
14405                 cv = function(v){
14406                     if(!v){
14407                         return '';
14408                     }
14409                     if(v instanceof Date){
14410                         return v;
14411                     }
14412                     if(dateFormat){
14413                         if(dateFormat == "timestamp"){
14414                             return new Date(v*1000);
14415                         }
14416                         return Date.parseDate(v, dateFormat);
14417                     }
14418                     var parsed = Date.parse(v);
14419                     return parsed ? new Date(parsed) : null;
14420                 };
14421              break;
14422             
14423         }
14424         this.convert = cv;
14425     }
14426 };
14427
14428 Roo.data.Field.prototype = {
14429     dateFormat: null,
14430     defaultValue: "",
14431     mapping: null,
14432     sortType : null,
14433     sortDir : "ASC"
14434 };/*
14435  * Based on:
14436  * Ext JS Library 1.1.1
14437  * Copyright(c) 2006-2007, Ext JS, LLC.
14438  *
14439  * Originally Released Under LGPL - original licence link has changed is not relivant.
14440  *
14441  * Fork - LGPL
14442  * <script type="text/javascript">
14443  */
14444  
14445 // Base class for reading structured data from a data source.  This class is intended to be
14446 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14447
14448 /**
14449  * @class Roo.data.DataReader
14450  * Base class for reading structured data from a data source.  This class is intended to be
14451  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14452  */
14453
14454 Roo.data.DataReader = function(meta, recordType){
14455     
14456     this.meta = meta;
14457     
14458     this.recordType = recordType instanceof Array ? 
14459         Roo.data.Record.create(recordType) : recordType;
14460 };
14461
14462 Roo.data.DataReader.prototype = {
14463     
14464     
14465     readerType : 'Data',
14466      /**
14467      * Create an empty record
14468      * @param {Object} data (optional) - overlay some values
14469      * @return {Roo.data.Record} record created.
14470      */
14471     newRow :  function(d) {
14472         var da =  {};
14473         this.recordType.prototype.fields.each(function(c) {
14474             switch( c.type) {
14475                 case 'int' : da[c.name] = 0; break;
14476                 case 'date' : da[c.name] = new Date(); break;
14477                 case 'float' : da[c.name] = 0.0; break;
14478                 case 'boolean' : da[c.name] = false; break;
14479                 default : da[c.name] = ""; break;
14480             }
14481             
14482         });
14483         return new this.recordType(Roo.apply(da, d));
14484     }
14485     
14486     
14487 };/*
14488  * Based on:
14489  * Ext JS Library 1.1.1
14490  * Copyright(c) 2006-2007, Ext JS, LLC.
14491  *
14492  * Originally Released Under LGPL - original licence link has changed is not relivant.
14493  *
14494  * Fork - LGPL
14495  * <script type="text/javascript">
14496  */
14497
14498 /**
14499  * @class Roo.data.DataProxy
14500  * @extends Roo.data.Observable
14501  * This class is an abstract base class for implementations which provide retrieval of
14502  * unformatted data objects.<br>
14503  * <p>
14504  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14505  * (of the appropriate type which knows how to parse the data object) to provide a block of
14506  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14507  * <p>
14508  * Custom implementations must implement the load method as described in
14509  * {@link Roo.data.HttpProxy#load}.
14510  */
14511 Roo.data.DataProxy = function(){
14512     this.addEvents({
14513         /**
14514          * @event beforeload
14515          * Fires before a network request is made to retrieve a data object.
14516          * @param {Object} This DataProxy object.
14517          * @param {Object} params The params parameter to the load function.
14518          */
14519         beforeload : true,
14520         /**
14521          * @event load
14522          * Fires before the load method's callback is called.
14523          * @param {Object} This DataProxy object.
14524          * @param {Object} o The data object.
14525          * @param {Object} arg The callback argument object passed to the load function.
14526          */
14527         load : true,
14528         /**
14529          * @event loadexception
14530          * Fires if an Exception occurs during data retrieval.
14531          * @param {Object} This DataProxy object.
14532          * @param {Object} o The data object.
14533          * @param {Object} arg The callback argument object passed to the load function.
14534          * @param {Object} e The Exception.
14535          */
14536         loadexception : true
14537     });
14538     Roo.data.DataProxy.superclass.constructor.call(this);
14539 };
14540
14541 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14542
14543     /**
14544      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14545      */
14546 /*
14547  * Based on:
14548  * Ext JS Library 1.1.1
14549  * Copyright(c) 2006-2007, Ext JS, LLC.
14550  *
14551  * Originally Released Under LGPL - original licence link has changed is not relivant.
14552  *
14553  * Fork - LGPL
14554  * <script type="text/javascript">
14555  */
14556 /**
14557  * @class Roo.data.MemoryProxy
14558  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14559  * to the Reader when its load method is called.
14560  * @constructor
14561  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14562  */
14563 Roo.data.MemoryProxy = function(data){
14564     if (data.data) {
14565         data = data.data;
14566     }
14567     Roo.data.MemoryProxy.superclass.constructor.call(this);
14568     this.data = data;
14569 };
14570
14571 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14572     
14573     /**
14574      * Load data from the requested source (in this case an in-memory
14575      * data object passed to the constructor), read the data object into
14576      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14577      * process that block using the passed callback.
14578      * @param {Object} params This parameter is not used by the MemoryProxy class.
14579      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14580      * object into a block of Roo.data.Records.
14581      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14582      * The function must be passed <ul>
14583      * <li>The Record block object</li>
14584      * <li>The "arg" argument from the load function</li>
14585      * <li>A boolean success indicator</li>
14586      * </ul>
14587      * @param {Object} scope The scope in which to call the callback
14588      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14589      */
14590     load : function(params, reader, callback, scope, arg){
14591         params = params || {};
14592         var result;
14593         try {
14594             result = reader.readRecords(params.data ? params.data :this.data);
14595         }catch(e){
14596             this.fireEvent("loadexception", this, arg, null, e);
14597             callback.call(scope, null, arg, false);
14598             return;
14599         }
14600         callback.call(scope, result, arg, true);
14601     },
14602     
14603     // private
14604     update : function(params, records){
14605         
14606     }
14607 });/*
14608  * Based on:
14609  * Ext JS Library 1.1.1
14610  * Copyright(c) 2006-2007, Ext JS, LLC.
14611  *
14612  * Originally Released Under LGPL - original licence link has changed is not relivant.
14613  *
14614  * Fork - LGPL
14615  * <script type="text/javascript">
14616  */
14617 /**
14618  * @class Roo.data.HttpProxy
14619  * @extends Roo.data.DataProxy
14620  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14621  * configured to reference a certain URL.<br><br>
14622  * <p>
14623  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14624  * from which the running page was served.<br><br>
14625  * <p>
14626  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14627  * <p>
14628  * Be aware that to enable the browser to parse an XML document, the server must set
14629  * the Content-Type header in the HTTP response to "text/xml".
14630  * @constructor
14631  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14632  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14633  * will be used to make the request.
14634  */
14635 Roo.data.HttpProxy = function(conn){
14636     Roo.data.HttpProxy.superclass.constructor.call(this);
14637     // is conn a conn config or a real conn?
14638     this.conn = conn;
14639     this.useAjax = !conn || !conn.events;
14640   
14641 };
14642
14643 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14644     // thse are take from connection...
14645     
14646     /**
14647      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14648      */
14649     /**
14650      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14651      * extra parameters to each request made by this object. (defaults to undefined)
14652      */
14653     /**
14654      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14655      *  to each request made by this object. (defaults to undefined)
14656      */
14657     /**
14658      * @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)
14659      */
14660     /**
14661      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14662      */
14663      /**
14664      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14665      * @type Boolean
14666      */
14667   
14668
14669     /**
14670      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14671      * @type Boolean
14672      */
14673     /**
14674      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14675      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14676      * a finer-grained basis than the DataProxy events.
14677      */
14678     getConnection : function(){
14679         return this.useAjax ? Roo.Ajax : this.conn;
14680     },
14681
14682     /**
14683      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14684      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14685      * process that block using the passed callback.
14686      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14687      * for the request to the remote server.
14688      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14689      * object into a block of Roo.data.Records.
14690      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14691      * The function must be passed <ul>
14692      * <li>The Record block object</li>
14693      * <li>The "arg" argument from the load function</li>
14694      * <li>A boolean success indicator</li>
14695      * </ul>
14696      * @param {Object} scope The scope in which to call the callback
14697      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14698      */
14699     load : function(params, reader, callback, scope, arg){
14700         if(this.fireEvent("beforeload", this, params) !== false){
14701             var  o = {
14702                 params : params || {},
14703                 request: {
14704                     callback : callback,
14705                     scope : scope,
14706                     arg : arg
14707                 },
14708                 reader: reader,
14709                 callback : this.loadResponse,
14710                 scope: this
14711             };
14712             if(this.useAjax){
14713                 Roo.applyIf(o, this.conn);
14714                 if(this.activeRequest){
14715                     Roo.Ajax.abort(this.activeRequest);
14716                 }
14717                 this.activeRequest = Roo.Ajax.request(o);
14718             }else{
14719                 this.conn.request(o);
14720             }
14721         }else{
14722             callback.call(scope||this, null, arg, false);
14723         }
14724     },
14725
14726     // private
14727     loadResponse : function(o, success, response){
14728         delete this.activeRequest;
14729         if(!success){
14730             this.fireEvent("loadexception", this, o, response);
14731             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14732             return;
14733         }
14734         var result;
14735         try {
14736             result = o.reader.read(response);
14737         }catch(e){
14738             this.fireEvent("loadexception", this, o, response, e);
14739             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14740             return;
14741         }
14742         
14743         this.fireEvent("load", this, o, o.request.arg);
14744         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14745     },
14746
14747     // private
14748     update : function(dataSet){
14749
14750     },
14751
14752     // private
14753     updateResponse : function(dataSet){
14754
14755     }
14756 });/*
14757  * Based on:
14758  * Ext JS Library 1.1.1
14759  * Copyright(c) 2006-2007, Ext JS, LLC.
14760  *
14761  * Originally Released Under LGPL - original licence link has changed is not relivant.
14762  *
14763  * Fork - LGPL
14764  * <script type="text/javascript">
14765  */
14766
14767 /**
14768  * @class Roo.data.ScriptTagProxy
14769  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14770  * other than the originating domain of the running page.<br><br>
14771  * <p>
14772  * <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
14773  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14774  * <p>
14775  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14776  * source code that is used as the source inside a &lt;script> tag.<br><br>
14777  * <p>
14778  * In order for the browser to process the returned data, the server must wrap the data object
14779  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14780  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14781  * depending on whether the callback name was passed:
14782  * <p>
14783  * <pre><code>
14784 boolean scriptTag = false;
14785 String cb = request.getParameter("callback");
14786 if (cb != null) {
14787     scriptTag = true;
14788     response.setContentType("text/javascript");
14789 } else {
14790     response.setContentType("application/x-json");
14791 }
14792 Writer out = response.getWriter();
14793 if (scriptTag) {
14794     out.write(cb + "(");
14795 }
14796 out.print(dataBlock.toJsonString());
14797 if (scriptTag) {
14798     out.write(");");
14799 }
14800 </pre></code>
14801  *
14802  * @constructor
14803  * @param {Object} config A configuration object.
14804  */
14805 Roo.data.ScriptTagProxy = function(config){
14806     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14807     Roo.apply(this, config);
14808     this.head = document.getElementsByTagName("head")[0];
14809 };
14810
14811 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14812
14813 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14814     /**
14815      * @cfg {String} url The URL from which to request the data object.
14816      */
14817     /**
14818      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14819      */
14820     timeout : 30000,
14821     /**
14822      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14823      * the server the name of the callback function set up by the load call to process the returned data object.
14824      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14825      * javascript output which calls this named function passing the data object as its only parameter.
14826      */
14827     callbackParam : "callback",
14828     /**
14829      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14830      * name to the request.
14831      */
14832     nocache : true,
14833
14834     /**
14835      * Load data from the configured URL, read the data object into
14836      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14837      * process that block using the passed callback.
14838      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14839      * for the request to the remote server.
14840      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14841      * object into a block of Roo.data.Records.
14842      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14843      * The function must be passed <ul>
14844      * <li>The Record block object</li>
14845      * <li>The "arg" argument from the load function</li>
14846      * <li>A boolean success indicator</li>
14847      * </ul>
14848      * @param {Object} scope The scope in which to call the callback
14849      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14850      */
14851     load : function(params, reader, callback, scope, arg){
14852         if(this.fireEvent("beforeload", this, params) !== false){
14853
14854             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14855
14856             var url = this.url;
14857             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14858             if(this.nocache){
14859                 url += "&_dc=" + (new Date().getTime());
14860             }
14861             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14862             var trans = {
14863                 id : transId,
14864                 cb : "stcCallback"+transId,
14865                 scriptId : "stcScript"+transId,
14866                 params : params,
14867                 arg : arg,
14868                 url : url,
14869                 callback : callback,
14870                 scope : scope,
14871                 reader : reader
14872             };
14873             var conn = this;
14874
14875             window[trans.cb] = function(o){
14876                 conn.handleResponse(o, trans);
14877             };
14878
14879             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14880
14881             if(this.autoAbort !== false){
14882                 this.abort();
14883             }
14884
14885             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14886
14887             var script = document.createElement("script");
14888             script.setAttribute("src", url);
14889             script.setAttribute("type", "text/javascript");
14890             script.setAttribute("id", trans.scriptId);
14891             this.head.appendChild(script);
14892
14893             this.trans = trans;
14894         }else{
14895             callback.call(scope||this, null, arg, false);
14896         }
14897     },
14898
14899     // private
14900     isLoading : function(){
14901         return this.trans ? true : false;
14902     },
14903
14904     /**
14905      * Abort the current server request.
14906      */
14907     abort : function(){
14908         if(this.isLoading()){
14909             this.destroyTrans(this.trans);
14910         }
14911     },
14912
14913     // private
14914     destroyTrans : function(trans, isLoaded){
14915         this.head.removeChild(document.getElementById(trans.scriptId));
14916         clearTimeout(trans.timeoutId);
14917         if(isLoaded){
14918             window[trans.cb] = undefined;
14919             try{
14920                 delete window[trans.cb];
14921             }catch(e){}
14922         }else{
14923             // if hasn't been loaded, wait for load to remove it to prevent script error
14924             window[trans.cb] = function(){
14925                 window[trans.cb] = undefined;
14926                 try{
14927                     delete window[trans.cb];
14928                 }catch(e){}
14929             };
14930         }
14931     },
14932
14933     // private
14934     handleResponse : function(o, trans){
14935         this.trans = false;
14936         this.destroyTrans(trans, true);
14937         var result;
14938         try {
14939             result = trans.reader.readRecords(o);
14940         }catch(e){
14941             this.fireEvent("loadexception", this, o, trans.arg, e);
14942             trans.callback.call(trans.scope||window, null, trans.arg, false);
14943             return;
14944         }
14945         this.fireEvent("load", this, o, trans.arg);
14946         trans.callback.call(trans.scope||window, result, trans.arg, true);
14947     },
14948
14949     // private
14950     handleFailure : function(trans){
14951         this.trans = false;
14952         this.destroyTrans(trans, false);
14953         this.fireEvent("loadexception", this, null, trans.arg);
14954         trans.callback.call(trans.scope||window, null, trans.arg, false);
14955     }
14956 });/*
14957  * Based on:
14958  * Ext JS Library 1.1.1
14959  * Copyright(c) 2006-2007, Ext JS, LLC.
14960  *
14961  * Originally Released Under LGPL - original licence link has changed is not relivant.
14962  *
14963  * Fork - LGPL
14964  * <script type="text/javascript">
14965  */
14966
14967 /**
14968  * @class Roo.data.JsonReader
14969  * @extends Roo.data.DataReader
14970  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14971  * based on mappings in a provided Roo.data.Record constructor.
14972  * 
14973  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14974  * in the reply previously. 
14975  * 
14976  * <p>
14977  * Example code:
14978  * <pre><code>
14979 var RecordDef = Roo.data.Record.create([
14980     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14981     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14982 ]);
14983 var myReader = new Roo.data.JsonReader({
14984     totalProperty: "results",    // The property which contains the total dataset size (optional)
14985     root: "rows",                // The property which contains an Array of row objects
14986     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14987 }, RecordDef);
14988 </code></pre>
14989  * <p>
14990  * This would consume a JSON file like this:
14991  * <pre><code>
14992 { 'results': 2, 'rows': [
14993     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14994     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14995 }
14996 </code></pre>
14997  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14998  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14999  * paged from the remote server.
15000  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15001  * @cfg {String} root name of the property which contains the Array of row objects.
15002  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15003  * @cfg {Array} fields Array of field definition objects
15004  * @constructor
15005  * Create a new JsonReader
15006  * @param {Object} meta Metadata configuration options
15007  * @param {Object} recordType Either an Array of field definition objects,
15008  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15009  */
15010 Roo.data.JsonReader = function(meta, recordType){
15011     
15012     meta = meta || {};
15013     // set some defaults:
15014     Roo.applyIf(meta, {
15015         totalProperty: 'total',
15016         successProperty : 'success',
15017         root : 'data',
15018         id : 'id'
15019     });
15020     
15021     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15022 };
15023 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15024     
15025     readerType : 'Json',
15026     
15027     /**
15028      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15029      * Used by Store query builder to append _requestMeta to params.
15030      * 
15031      */
15032     metaFromRemote : false,
15033     /**
15034      * This method is only used by a DataProxy which has retrieved data from a remote server.
15035      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15036      * @return {Object} data A data block which is used by an Roo.data.Store object as
15037      * a cache of Roo.data.Records.
15038      */
15039     read : function(response){
15040         var json = response.responseText;
15041        
15042         var o = /* eval:var:o */ eval("("+json+")");
15043         if(!o) {
15044             throw {message: "JsonReader.read: Json object not found"};
15045         }
15046         
15047         if(o.metaData){
15048             
15049             delete this.ef;
15050             this.metaFromRemote = true;
15051             this.meta = o.metaData;
15052             this.recordType = Roo.data.Record.create(o.metaData.fields);
15053             this.onMetaChange(this.meta, this.recordType, o);
15054         }
15055         return this.readRecords(o);
15056     },
15057
15058     // private function a store will implement
15059     onMetaChange : function(meta, recordType, o){
15060
15061     },
15062
15063     /**
15064          * @ignore
15065          */
15066     simpleAccess: function(obj, subsc) {
15067         return obj[subsc];
15068     },
15069
15070         /**
15071          * @ignore
15072          */
15073     getJsonAccessor: function(){
15074         var re = /[\[\.]/;
15075         return function(expr) {
15076             try {
15077                 return(re.test(expr))
15078                     ? new Function("obj", "return obj." + expr)
15079                     : function(obj){
15080                         return obj[expr];
15081                     };
15082             } catch(e){}
15083             return Roo.emptyFn;
15084         };
15085     }(),
15086
15087     /**
15088      * Create a data block containing Roo.data.Records from an XML document.
15089      * @param {Object} o An object which contains an Array of row objects in the property specified
15090      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15091      * which contains the total size of the dataset.
15092      * @return {Object} data A data block which is used by an Roo.data.Store object as
15093      * a cache of Roo.data.Records.
15094      */
15095     readRecords : function(o){
15096         /**
15097          * After any data loads, the raw JSON data is available for further custom processing.
15098          * @type Object
15099          */
15100         this.o = o;
15101         var s = this.meta, Record = this.recordType,
15102             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15103
15104 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15105         if (!this.ef) {
15106             if(s.totalProperty) {
15107                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15108                 }
15109                 if(s.successProperty) {
15110                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15111                 }
15112                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15113                 if (s.id) {
15114                         var g = this.getJsonAccessor(s.id);
15115                         this.getId = function(rec) {
15116                                 var r = g(rec);  
15117                                 return (r === undefined || r === "") ? null : r;
15118                         };
15119                 } else {
15120                         this.getId = function(){return null;};
15121                 }
15122             this.ef = [];
15123             for(var jj = 0; jj < fl; jj++){
15124                 f = fi[jj];
15125                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15126                 this.ef[jj] = this.getJsonAccessor(map);
15127             }
15128         }
15129
15130         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15131         if(s.totalProperty){
15132             var vt = parseInt(this.getTotal(o), 10);
15133             if(!isNaN(vt)){
15134                 totalRecords = vt;
15135             }
15136         }
15137         if(s.successProperty){
15138             var vs = this.getSuccess(o);
15139             if(vs === false || vs === 'false'){
15140                 success = false;
15141             }
15142         }
15143         var records = [];
15144         for(var i = 0; i < c; i++){
15145                 var n = root[i];
15146             var values = {};
15147             var id = this.getId(n);
15148             for(var j = 0; j < fl; j++){
15149                 f = fi[j];
15150             var v = this.ef[j](n);
15151             if (!f.convert) {
15152                 Roo.log('missing convert for ' + f.name);
15153                 Roo.log(f);
15154                 continue;
15155             }
15156             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15157             }
15158             var record = new Record(values, id);
15159             record.json = n;
15160             records[i] = record;
15161         }
15162         return {
15163             raw : o,
15164             success : success,
15165             records : records,
15166             totalRecords : totalRecords
15167         };
15168     },
15169     // used when loading children.. @see loadDataFromChildren
15170     toLoadData: function(rec)
15171     {
15172         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15173         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15174         return { data : data, total : data.length };
15175         
15176     }
15177 });/*
15178  * Based on:
15179  * Ext JS Library 1.1.1
15180  * Copyright(c) 2006-2007, Ext JS, LLC.
15181  *
15182  * Originally Released Under LGPL - original licence link has changed is not relivant.
15183  *
15184  * Fork - LGPL
15185  * <script type="text/javascript">
15186  */
15187
15188 /**
15189  * @class Roo.data.ArrayReader
15190  * @extends Roo.data.DataReader
15191  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15192  * Each element of that Array represents a row of data fields. The
15193  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15194  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15195  * <p>
15196  * Example code:.
15197  * <pre><code>
15198 var RecordDef = Roo.data.Record.create([
15199     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15200     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15201 ]);
15202 var myReader = new Roo.data.ArrayReader({
15203     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15204 }, RecordDef);
15205 </code></pre>
15206  * <p>
15207  * This would consume an Array like this:
15208  * <pre><code>
15209 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15210   </code></pre>
15211  
15212  * @constructor
15213  * Create a new JsonReader
15214  * @param {Object} meta Metadata configuration options.
15215  * @param {Object|Array} recordType Either an Array of field definition objects
15216  * 
15217  * @cfg {Array} fields Array of field definition objects
15218  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15219  * as specified to {@link Roo.data.Record#create},
15220  * or an {@link Roo.data.Record} object
15221  *
15222  * 
15223  * created using {@link Roo.data.Record#create}.
15224  */
15225 Roo.data.ArrayReader = function(meta, recordType)
15226 {    
15227     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15228 };
15229
15230 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15231     
15232       /**
15233      * Create a data block containing Roo.data.Records from an XML document.
15234      * @param {Object} o An Array of row objects which represents the dataset.
15235      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15236      * a cache of Roo.data.Records.
15237      */
15238     readRecords : function(o)
15239     {
15240         var sid = this.meta ? this.meta.id : null;
15241         var recordType = this.recordType, fields = recordType.prototype.fields;
15242         var records = [];
15243         var root = o;
15244         for(var i = 0; i < root.length; i++){
15245             var n = root[i];
15246             var values = {};
15247             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15248             for(var j = 0, jlen = fields.length; j < jlen; j++){
15249                 var f = fields.items[j];
15250                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15251                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15252                 v = f.convert(v);
15253                 values[f.name] = v;
15254             }
15255             var record = new recordType(values, id);
15256             record.json = n;
15257             records[records.length] = record;
15258         }
15259         return {
15260             records : records,
15261             totalRecords : records.length
15262         };
15263     },
15264     // used when loading children.. @see loadDataFromChildren
15265     toLoadData: function(rec)
15266     {
15267         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15268         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15269         
15270     }
15271     
15272     
15273 });/*
15274  * - LGPL
15275  * * 
15276  */
15277
15278 /**
15279  * @class Roo.bootstrap.ComboBox
15280  * @extends Roo.bootstrap.TriggerField
15281  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15282  * @cfg {Boolean} append (true|false) default false
15283  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15284  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15285  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15286  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15287  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15288  * @cfg {Boolean} animate default true
15289  * @cfg {Boolean} emptyResultText only for touch device
15290  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15291  * @cfg {String} emptyTitle default ''
15292  * @cfg {Number} width fixed with? experimental
15293  * @constructor
15294  * Create a new ComboBox.
15295  * @param {Object} config Configuration options
15296  */
15297 Roo.bootstrap.ComboBox = function(config){
15298     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15299     this.addEvents({
15300         /**
15301          * @event expand
15302          * Fires when the dropdown list is expanded
15303         * @param {Roo.bootstrap.ComboBox} combo This combo box
15304         */
15305         'expand' : true,
15306         /**
15307          * @event collapse
15308          * Fires when the dropdown list is collapsed
15309         * @param {Roo.bootstrap.ComboBox} combo This combo box
15310         */
15311         'collapse' : true,
15312         /**
15313          * @event beforeselect
15314          * Fires before a list item is selected. Return false to cancel the selection.
15315         * @param {Roo.bootstrap.ComboBox} combo This combo box
15316         * @param {Roo.data.Record} record The data record returned from the underlying store
15317         * @param {Number} index The index of the selected item in the dropdown list
15318         */
15319         'beforeselect' : true,
15320         /**
15321          * @event select
15322          * Fires when a list item is selected
15323         * @param {Roo.bootstrap.ComboBox} combo This combo box
15324         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15325         * @param {Number} index The index of the selected item in the dropdown list
15326         */
15327         'select' : true,
15328         /**
15329          * @event beforequery
15330          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15331          * The event object passed has these properties:
15332         * @param {Roo.bootstrap.ComboBox} combo This combo box
15333         * @param {String} query The query
15334         * @param {Boolean} forceAll true to force "all" query
15335         * @param {Boolean} cancel true to cancel the query
15336         * @param {Object} e The query event object
15337         */
15338         'beforequery': true,
15339          /**
15340          * @event add
15341          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15342         * @param {Roo.bootstrap.ComboBox} combo This combo box
15343         */
15344         'add' : true,
15345         /**
15346          * @event edit
15347          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15348         * @param {Roo.bootstrap.ComboBox} combo This combo box
15349         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15350         */
15351         'edit' : true,
15352         /**
15353          * @event remove
15354          * Fires when the remove value from the combobox array
15355         * @param {Roo.bootstrap.ComboBox} combo This combo box
15356         */
15357         'remove' : true,
15358         /**
15359          * @event afterremove
15360          * Fires when the remove value from the combobox array
15361         * @param {Roo.bootstrap.ComboBox} combo This combo box
15362         */
15363         'afterremove' : true,
15364         /**
15365          * @event specialfilter
15366          * Fires when specialfilter
15367             * @param {Roo.bootstrap.ComboBox} combo This combo box
15368             */
15369         'specialfilter' : true,
15370         /**
15371          * @event tick
15372          * Fires when tick the element
15373             * @param {Roo.bootstrap.ComboBox} combo This combo box
15374             */
15375         'tick' : true,
15376         /**
15377          * @event touchviewdisplay
15378          * Fires when touch view require special display (default is using displayField)
15379             * @param {Roo.bootstrap.ComboBox} combo This combo box
15380             * @param {Object} cfg set html .
15381             */
15382         'touchviewdisplay' : true
15383         
15384     });
15385     
15386     this.item = [];
15387     this.tickItems = [];
15388     
15389     this.selectedIndex = -1;
15390     if(this.mode == 'local'){
15391         if(config.queryDelay === undefined){
15392             this.queryDelay = 10;
15393         }
15394         if(config.minChars === undefined){
15395             this.minChars = 0;
15396         }
15397     }
15398 };
15399
15400 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15401      
15402     /**
15403      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15404      * rendering into an Roo.Editor, defaults to false)
15405      */
15406     /**
15407      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15408      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15409      */
15410     /**
15411      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15412      */
15413     /**
15414      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15415      * the dropdown list (defaults to undefined, with no header element)
15416      */
15417
15418      /**
15419      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15420      */
15421      
15422      /**
15423      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15424      */
15425     listWidth: undefined,
15426     /**
15427      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15428      * mode = 'remote' or 'text' if mode = 'local')
15429      */
15430     displayField: undefined,
15431     
15432     /**
15433      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15434      * mode = 'remote' or 'value' if mode = 'local'). 
15435      * Note: use of a valueField requires the user make a selection
15436      * in order for a value to be mapped.
15437      */
15438     valueField: undefined,
15439     /**
15440      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15441      */
15442     modalTitle : '',
15443     
15444     /**
15445      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15446      * field's data value (defaults to the underlying DOM element's name)
15447      */
15448     hiddenName: undefined,
15449     /**
15450      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15451      */
15452     listClass: '',
15453     /**
15454      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15455      */
15456     selectedClass: 'active',
15457     
15458     /**
15459      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15460      */
15461     shadow:'sides',
15462     /**
15463      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15464      * anchor positions (defaults to 'tl-bl')
15465      */
15466     listAlign: 'tl-bl?',
15467     /**
15468      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15469      */
15470     maxHeight: 300,
15471     /**
15472      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15473      * query specified by the allQuery config option (defaults to 'query')
15474      */
15475     triggerAction: 'query',
15476     /**
15477      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15478      * (defaults to 4, does not apply if editable = false)
15479      */
15480     minChars : 4,
15481     /**
15482      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15483      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15484      */
15485     typeAhead: false,
15486     /**
15487      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15488      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15489      */
15490     queryDelay: 500,
15491     /**
15492      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15493      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15494      */
15495     pageSize: 0,
15496     /**
15497      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15498      * when editable = true (defaults to false)
15499      */
15500     selectOnFocus:false,
15501     /**
15502      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15503      */
15504     queryParam: 'query',
15505     /**
15506      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15507      * when mode = 'remote' (defaults to 'Loading...')
15508      */
15509     loadingText: 'Loading...',
15510     /**
15511      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15512      */
15513     resizable: false,
15514     /**
15515      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15516      */
15517     handleHeight : 8,
15518     /**
15519      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15520      * traditional select (defaults to true)
15521      */
15522     editable: true,
15523     /**
15524      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15525      */
15526     allQuery: '',
15527     /**
15528      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15529      */
15530     mode: 'remote',
15531     /**
15532      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15533      * listWidth has a higher value)
15534      */
15535     minListWidth : 70,
15536     /**
15537      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15538      * allow the user to set arbitrary text into the field (defaults to false)
15539      */
15540     forceSelection:false,
15541     /**
15542      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15543      * if typeAhead = true (defaults to 250)
15544      */
15545     typeAheadDelay : 250,
15546     /**
15547      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15548      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15549      */
15550     valueNotFoundText : undefined,
15551     /**
15552      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15553      */
15554     blockFocus : false,
15555     
15556     /**
15557      * @cfg {Boolean} disableClear Disable showing of clear button.
15558      */
15559     disableClear : false,
15560     /**
15561      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15562      */
15563     alwaysQuery : false,
15564     
15565     /**
15566      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15567      */
15568     multiple : false,
15569     
15570     /**
15571      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15572      */
15573     invalidClass : "has-warning",
15574     
15575     /**
15576      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15577      */
15578     validClass : "has-success",
15579     
15580     /**
15581      * @cfg {Boolean} specialFilter (true|false) special filter default false
15582      */
15583     specialFilter : false,
15584     
15585     /**
15586      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15587      */
15588     mobileTouchView : true,
15589     
15590     /**
15591      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15592      */
15593     useNativeIOS : false,
15594     
15595     /**
15596      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15597      */
15598     mobile_restrict_height : false,
15599     
15600     ios_options : false,
15601     
15602     //private
15603     addicon : false,
15604     editicon: false,
15605     
15606     page: 0,
15607     hasQuery: false,
15608     append: false,
15609     loadNext: false,
15610     autoFocus : true,
15611     tickable : false,
15612     btnPosition : 'right',
15613     triggerList : true,
15614     showToggleBtn : true,
15615     animate : true,
15616     emptyResultText: 'Empty',
15617     triggerText : 'Select',
15618     emptyTitle : '',
15619     width : false,
15620     
15621     // element that contains real text value.. (when hidden is used..)
15622     
15623     getAutoCreate : function()
15624     {   
15625         var cfg = false;
15626         //render
15627         /*
15628          * Render classic select for iso
15629          */
15630         
15631         if(Roo.isIOS && this.useNativeIOS){
15632             cfg = this.getAutoCreateNativeIOS();
15633             return cfg;
15634         }
15635         
15636         /*
15637          * Touch Devices
15638          */
15639         
15640         if(Roo.isTouch && this.mobileTouchView){
15641             cfg = this.getAutoCreateTouchView();
15642             return cfg;;
15643         }
15644         
15645         /*
15646          *  Normal ComboBox
15647          */
15648         if(!this.tickable){
15649             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15650             return cfg;
15651         }
15652         
15653         /*
15654          *  ComboBox with tickable selections
15655          */
15656              
15657         var align = this.labelAlign || this.parentLabelAlign();
15658         
15659         cfg = {
15660             cls : 'form-group roo-combobox-tickable' //input-group
15661         };
15662         
15663         var btn_text_select = '';
15664         var btn_text_done = '';
15665         var btn_text_cancel = '';
15666         
15667         if (this.btn_text_show) {
15668             btn_text_select = 'Select';
15669             btn_text_done = 'Done';
15670             btn_text_cancel = 'Cancel'; 
15671         }
15672         
15673         var buttons = {
15674             tag : 'div',
15675             cls : 'tickable-buttons',
15676             cn : [
15677                 {
15678                     tag : 'button',
15679                     type : 'button',
15680                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15681                     //html : this.triggerText
15682                     html: btn_text_select
15683                 },
15684                 {
15685                     tag : 'button',
15686                     type : 'button',
15687                     name : 'ok',
15688                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15689                     //html : 'Done'
15690                     html: btn_text_done
15691                 },
15692                 {
15693                     tag : 'button',
15694                     type : 'button',
15695                     name : 'cancel',
15696                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15697                     //html : 'Cancel'
15698                     html: btn_text_cancel
15699                 }
15700             ]
15701         };
15702         
15703         if(this.editable){
15704             buttons.cn.unshift({
15705                 tag: 'input',
15706                 cls: 'roo-select2-search-field-input'
15707             });
15708         }
15709         
15710         var _this = this;
15711         
15712         Roo.each(buttons.cn, function(c){
15713             if (_this.size) {
15714                 c.cls += ' btn-' + _this.size;
15715             }
15716
15717             if (_this.disabled) {
15718                 c.disabled = true;
15719             }
15720         });
15721         
15722         var box = {
15723             tag: 'div',
15724             style : 'display: contents',
15725             cn: [
15726                 {
15727                     tag: 'input',
15728                     type : 'hidden',
15729                     cls: 'form-hidden-field'
15730                 },
15731                 {
15732                     tag: 'ul',
15733                     cls: 'roo-select2-choices',
15734                     cn:[
15735                         {
15736                             tag: 'li',
15737                             cls: 'roo-select2-search-field',
15738                             cn: [
15739                                 buttons
15740                             ]
15741                         }
15742                     ]
15743                 }
15744             ]
15745         };
15746         
15747         var combobox = {
15748             cls: 'roo-select2-container input-group roo-select2-container-multi',
15749             cn: [
15750                 
15751                 box
15752 //                {
15753 //                    tag: 'ul',
15754 //                    cls: 'typeahead typeahead-long dropdown-menu',
15755 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15756 //                }
15757             ]
15758         };
15759         
15760         if(this.hasFeedback && !this.allowBlank){
15761             
15762             var feedback = {
15763                 tag: 'span',
15764                 cls: 'glyphicon form-control-feedback'
15765             };
15766
15767             combobox.cn.push(feedback);
15768         }
15769         
15770         
15771         
15772         var indicator = {
15773             tag : 'i',
15774             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15775             tooltip : 'This field is required'
15776         };
15777         if (Roo.bootstrap.version == 4) {
15778             indicator = {
15779                 tag : 'i',
15780                 style : 'display:none'
15781             };
15782         }
15783         if (align ==='left' && this.fieldLabel.length) {
15784             
15785             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15786             
15787             cfg.cn = [
15788                 indicator,
15789                 {
15790                     tag: 'label',
15791                     'for' :  id,
15792                     cls : 'control-label col-form-label',
15793                     html : this.fieldLabel
15794
15795                 },
15796                 {
15797                     cls : "", 
15798                     cn: [
15799                         combobox
15800                     ]
15801                 }
15802
15803             ];
15804             
15805             var labelCfg = cfg.cn[1];
15806             var contentCfg = cfg.cn[2];
15807             
15808
15809             if(this.indicatorpos == 'right'){
15810                 
15811                 cfg.cn = [
15812                     {
15813                         tag: 'label',
15814                         'for' :  id,
15815                         cls : 'control-label col-form-label',
15816                         cn : [
15817                             {
15818                                 tag : 'span',
15819                                 html : this.fieldLabel
15820                             },
15821                             indicator
15822                         ]
15823                     },
15824                     {
15825                         cls : "",
15826                         cn: [
15827                             combobox
15828                         ]
15829                     }
15830
15831                 ];
15832                 
15833                 
15834                 
15835                 labelCfg = cfg.cn[0];
15836                 contentCfg = cfg.cn[1];
15837             
15838             }
15839             
15840             if(this.labelWidth > 12){
15841                 labelCfg.style = "width: " + this.labelWidth + 'px';
15842             }
15843             if(this.width * 1 > 0){
15844                 contentCfg.style = "width: " + this.width + 'px';
15845             }
15846             if(this.labelWidth < 13 && this.labelmd == 0){
15847                 this.labelmd = this.labelWidth;
15848             }
15849             
15850             if(this.labellg > 0){
15851                 labelCfg.cls += ' col-lg-' + this.labellg;
15852                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15853             }
15854             
15855             if(this.labelmd > 0){
15856                 labelCfg.cls += ' col-md-' + this.labelmd;
15857                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15858             }
15859             
15860             if(this.labelsm > 0){
15861                 labelCfg.cls += ' col-sm-' + this.labelsm;
15862                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15863             }
15864             
15865             if(this.labelxs > 0){
15866                 labelCfg.cls += ' col-xs-' + this.labelxs;
15867                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15868             }
15869                 
15870                 
15871         } else if ( this.fieldLabel.length) {
15872 //                Roo.log(" label");
15873                  cfg.cn = [
15874                    indicator,
15875                     {
15876                         tag: 'label',
15877                         //cls : 'input-group-addon',
15878                         html : this.fieldLabel
15879                     },
15880                     combobox
15881                 ];
15882                 
15883                 if(this.indicatorpos == 'right'){
15884                     cfg.cn = [
15885                         {
15886                             tag: 'label',
15887                             //cls : 'input-group-addon',
15888                             html : this.fieldLabel
15889                         },
15890                         indicator,
15891                         combobox
15892                     ];
15893                     
15894                 }
15895
15896         } else {
15897             
15898 //                Roo.log(" no label && no align");
15899                 cfg = combobox
15900                      
15901                 
15902         }
15903          
15904         var settings=this;
15905         ['xs','sm','md','lg'].map(function(size){
15906             if (settings[size]) {
15907                 cfg.cls += ' col-' + size + '-' + settings[size];
15908             }
15909         });
15910         
15911         return cfg;
15912         
15913     },
15914     
15915     _initEventsCalled : false,
15916     
15917     // private
15918     initEvents: function()
15919     {   
15920         if (this._initEventsCalled) { // as we call render... prevent looping...
15921             return;
15922         }
15923         this._initEventsCalled = true;
15924         
15925         if (!this.store) {
15926             throw "can not find store for combo";
15927         }
15928         
15929         this.indicator = this.indicatorEl();
15930         
15931         this.store = Roo.factory(this.store, Roo.data);
15932         this.store.parent = this;
15933         
15934         // if we are building from html. then this element is so complex, that we can not really
15935         // use the rendered HTML.
15936         // so we have to trash and replace the previous code.
15937         if (Roo.XComponent.build_from_html) {
15938             // remove this element....
15939             var e = this.el.dom, k=0;
15940             while (e ) { e = e.previousSibling;  ++k;}
15941
15942             this.el.remove();
15943             
15944             this.el=false;
15945             this.rendered = false;
15946             
15947             this.render(this.parent().getChildContainer(true), k);
15948         }
15949         
15950         if(Roo.isIOS && this.useNativeIOS){
15951             this.initIOSView();
15952             return;
15953         }
15954         
15955         /*
15956          * Touch Devices
15957          */
15958         
15959         if(Roo.isTouch && this.mobileTouchView){
15960             this.initTouchView();
15961             return;
15962         }
15963         
15964         if(this.tickable){
15965             this.initTickableEvents();
15966             return;
15967         }
15968         
15969         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15970         
15971         if(this.hiddenName){
15972             
15973             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15974             
15975             this.hiddenField.dom.value =
15976                 this.hiddenValue !== undefined ? this.hiddenValue :
15977                 this.value !== undefined ? this.value : '';
15978
15979             // prevent input submission
15980             this.el.dom.removeAttribute('name');
15981             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15982              
15983              
15984         }
15985         //if(Roo.isGecko){
15986         //    this.el.dom.setAttribute('autocomplete', 'off');
15987         //}
15988         
15989         var cls = 'x-combo-list';
15990         
15991         //this.list = new Roo.Layer({
15992         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15993         //});
15994         
15995         var _this = this;
15996         
15997         (function(){
15998             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15999             _this.list.setWidth(lw);
16000         }).defer(100);
16001         
16002         this.list.on('mouseover', this.onViewOver, this);
16003         this.list.on('mousemove', this.onViewMove, this);
16004         this.list.on('scroll', this.onViewScroll, this);
16005         
16006         /*
16007         this.list.swallowEvent('mousewheel');
16008         this.assetHeight = 0;
16009
16010         if(this.title){
16011             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16012             this.assetHeight += this.header.getHeight();
16013         }
16014
16015         this.innerList = this.list.createChild({cls:cls+'-inner'});
16016         this.innerList.on('mouseover', this.onViewOver, this);
16017         this.innerList.on('mousemove', this.onViewMove, this);
16018         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16019         
16020         if(this.allowBlank && !this.pageSize && !this.disableClear){
16021             this.footer = this.list.createChild({cls:cls+'-ft'});
16022             this.pageTb = new Roo.Toolbar(this.footer);
16023            
16024         }
16025         if(this.pageSize){
16026             this.footer = this.list.createChild({cls:cls+'-ft'});
16027             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16028                     {pageSize: this.pageSize});
16029             
16030         }
16031         
16032         if (this.pageTb && this.allowBlank && !this.disableClear) {
16033             var _this = this;
16034             this.pageTb.add(new Roo.Toolbar.Fill(), {
16035                 cls: 'x-btn-icon x-btn-clear',
16036                 text: '&#160;',
16037                 handler: function()
16038                 {
16039                     _this.collapse();
16040                     _this.clearValue();
16041                     _this.onSelect(false, -1);
16042                 }
16043             });
16044         }
16045         if (this.footer) {
16046             this.assetHeight += this.footer.getHeight();
16047         }
16048         */
16049             
16050         if(!this.tpl){
16051             this.tpl = Roo.bootstrap.version == 4 ?
16052                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16053                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16054         }
16055
16056         this.view = new Roo.View(this.list, this.tpl, {
16057             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16058         });
16059         //this.view.wrapEl.setDisplayed(false);
16060         this.view.on('click', this.onViewClick, this);
16061         
16062         
16063         this.store.on('beforeload', this.onBeforeLoad, this);
16064         this.store.on('load', this.onLoad, this);
16065         this.store.on('loadexception', this.onLoadException, this);
16066         /*
16067         if(this.resizable){
16068             this.resizer = new Roo.Resizable(this.list,  {
16069                pinned:true, handles:'se'
16070             });
16071             this.resizer.on('resize', function(r, w, h){
16072                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16073                 this.listWidth = w;
16074                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16075                 this.restrictHeight();
16076             }, this);
16077             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16078         }
16079         */
16080         if(!this.editable){
16081             this.editable = true;
16082             this.setEditable(false);
16083         }
16084         
16085         /*
16086         
16087         if (typeof(this.events.add.listeners) != 'undefined') {
16088             
16089             this.addicon = this.wrap.createChild(
16090                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16091        
16092             this.addicon.on('click', function(e) {
16093                 this.fireEvent('add', this);
16094             }, this);
16095         }
16096         if (typeof(this.events.edit.listeners) != 'undefined') {
16097             
16098             this.editicon = this.wrap.createChild(
16099                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16100             if (this.addicon) {
16101                 this.editicon.setStyle('margin-left', '40px');
16102             }
16103             this.editicon.on('click', function(e) {
16104                 
16105                 // we fire even  if inothing is selected..
16106                 this.fireEvent('edit', this, this.lastData );
16107                 
16108             }, this);
16109         }
16110         */
16111         
16112         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16113             "up" : function(e){
16114                 this.inKeyMode = true;
16115                 this.selectPrev();
16116             },
16117
16118             "down" : function(e){
16119                 if(!this.isExpanded()){
16120                     this.onTriggerClick();
16121                 }else{
16122                     this.inKeyMode = true;
16123                     this.selectNext();
16124                 }
16125             },
16126
16127             "enter" : function(e){
16128 //                this.onViewClick();
16129                 //return true;
16130                 this.collapse();
16131                 
16132                 if(this.fireEvent("specialkey", this, e)){
16133                     this.onViewClick(false);
16134                 }
16135                 
16136                 return true;
16137             },
16138
16139             "esc" : function(e){
16140                 this.collapse();
16141             },
16142
16143             "tab" : function(e){
16144                 this.collapse();
16145                 
16146                 if(this.fireEvent("specialkey", this, e)){
16147                     this.onViewClick(false);
16148                 }
16149                 
16150                 return true;
16151             },
16152
16153             scope : this,
16154
16155             doRelay : function(foo, bar, hname){
16156                 if(hname == 'down' || this.scope.isExpanded()){
16157                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16158                 }
16159                 return true;
16160             },
16161
16162             forceKeyDown: true
16163         });
16164         
16165         
16166         this.queryDelay = Math.max(this.queryDelay || 10,
16167                 this.mode == 'local' ? 10 : 250);
16168         
16169         
16170         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16171         
16172         if(this.typeAhead){
16173             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16174         }
16175         if(this.editable !== false){
16176             this.inputEl().on("keyup", this.onKeyUp, this);
16177         }
16178         if(this.forceSelection){
16179             this.inputEl().on('blur', this.doForce, this);
16180         }
16181         
16182         if(this.multiple){
16183             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16184             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16185         }
16186     },
16187     
16188     initTickableEvents: function()
16189     {   
16190         this.createList();
16191         
16192         if(this.hiddenName){
16193             
16194             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16195             
16196             this.hiddenField.dom.value =
16197                 this.hiddenValue !== undefined ? this.hiddenValue :
16198                 this.value !== undefined ? this.value : '';
16199
16200             // prevent input submission
16201             this.el.dom.removeAttribute('name');
16202             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16203              
16204              
16205         }
16206         
16207 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16208         
16209         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16210         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16211         if(this.triggerList){
16212             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16213         }
16214          
16215         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16216         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16217         
16218         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16219         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16220         
16221         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16222         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16223         
16224         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16225         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16226         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16227         
16228         this.okBtn.hide();
16229         this.cancelBtn.hide();
16230         
16231         var _this = this;
16232         
16233         (function(){
16234             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16235             _this.list.setWidth(lw);
16236         }).defer(100);
16237         
16238         this.list.on('mouseover', this.onViewOver, this);
16239         this.list.on('mousemove', this.onViewMove, this);
16240         
16241         this.list.on('scroll', this.onViewScroll, this);
16242         
16243         if(!this.tpl){
16244             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16245                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16246         }
16247
16248         this.view = new Roo.View(this.list, this.tpl, {
16249             singleSelect:true,
16250             tickable:true,
16251             parent:this,
16252             store: this.store,
16253             selectedClass: this.selectedClass
16254         });
16255         
16256         //this.view.wrapEl.setDisplayed(false);
16257         this.view.on('click', this.onViewClick, this);
16258         
16259         
16260         
16261         this.store.on('beforeload', this.onBeforeLoad, this);
16262         this.store.on('load', this.onLoad, this);
16263         this.store.on('loadexception', this.onLoadException, this);
16264         
16265         if(this.editable){
16266             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16267                 "up" : function(e){
16268                     this.inKeyMode = true;
16269                     this.selectPrev();
16270                 },
16271
16272                 "down" : function(e){
16273                     this.inKeyMode = true;
16274                     this.selectNext();
16275                 },
16276
16277                 "enter" : function(e){
16278                     if(this.fireEvent("specialkey", this, e)){
16279                         this.onViewClick(false);
16280                     }
16281                     
16282                     return true;
16283                 },
16284
16285                 "esc" : function(e){
16286                     this.onTickableFooterButtonClick(e, false, false);
16287                 },
16288
16289                 "tab" : function(e){
16290                     this.fireEvent("specialkey", this, e);
16291                     
16292                     this.onTickableFooterButtonClick(e, false, false);
16293                     
16294                     return true;
16295                 },
16296
16297                 scope : this,
16298
16299                 doRelay : function(e, fn, key){
16300                     if(this.scope.isExpanded()){
16301                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16302                     }
16303                     return true;
16304                 },
16305
16306                 forceKeyDown: true
16307             });
16308         }
16309         
16310         this.queryDelay = Math.max(this.queryDelay || 10,
16311                 this.mode == 'local' ? 10 : 250);
16312         
16313         
16314         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16315         
16316         if(this.typeAhead){
16317             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16318         }
16319         
16320         if(this.editable !== false){
16321             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16322         }
16323         
16324         this.indicator = this.indicatorEl();
16325         
16326         if(this.indicator){
16327             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16328             this.indicator.hide();
16329         }
16330         
16331     },
16332
16333     onDestroy : function(){
16334         if(this.view){
16335             this.view.setStore(null);
16336             this.view.el.removeAllListeners();
16337             this.view.el.remove();
16338             this.view.purgeListeners();
16339         }
16340         if(this.list){
16341             this.list.dom.innerHTML  = '';
16342         }
16343         
16344         if(this.store){
16345             this.store.un('beforeload', this.onBeforeLoad, this);
16346             this.store.un('load', this.onLoad, this);
16347             this.store.un('loadexception', this.onLoadException, this);
16348         }
16349         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16350     },
16351
16352     // private
16353     fireKey : function(e){
16354         if(e.isNavKeyPress() && !this.list.isVisible()){
16355             this.fireEvent("specialkey", this, e);
16356         }
16357     },
16358
16359     // private
16360     onResize: function(w, h)
16361     {
16362         
16363         
16364 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16365 //        
16366 //        if(typeof w != 'number'){
16367 //            // we do not handle it!?!?
16368 //            return;
16369 //        }
16370 //        var tw = this.trigger.getWidth();
16371 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16372 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16373 //        var x = w - tw;
16374 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16375 //            
16376 //        //this.trigger.setStyle('left', x+'px');
16377 //        
16378 //        if(this.list && this.listWidth === undefined){
16379 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16380 //            this.list.setWidth(lw);
16381 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16382 //        }
16383         
16384     
16385         
16386     },
16387
16388     /**
16389      * Allow or prevent the user from directly editing the field text.  If false is passed,
16390      * the user will only be able to select from the items defined in the dropdown list.  This method
16391      * is the runtime equivalent of setting the 'editable' config option at config time.
16392      * @param {Boolean} value True to allow the user to directly edit the field text
16393      */
16394     setEditable : function(value){
16395         if(value == this.editable){
16396             return;
16397         }
16398         this.editable = value;
16399         if(!value){
16400             this.inputEl().dom.setAttribute('readOnly', true);
16401             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16402             this.inputEl().addClass('x-combo-noedit');
16403         }else{
16404             this.inputEl().dom.removeAttribute('readOnly');
16405             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16406             this.inputEl().removeClass('x-combo-noedit');
16407         }
16408     },
16409
16410     // private
16411     
16412     onBeforeLoad : function(combo,opts){
16413         if(!this.hasFocus){
16414             return;
16415         }
16416          if (!opts.add) {
16417             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16418          }
16419         this.restrictHeight();
16420         this.selectedIndex = -1;
16421     },
16422
16423     // private
16424     onLoad : function(){
16425         
16426         this.hasQuery = false;
16427         
16428         if(!this.hasFocus){
16429             return;
16430         }
16431         
16432         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16433             this.loading.hide();
16434         }
16435         
16436         if(this.store.getCount() > 0){
16437             
16438             this.expand();
16439             this.restrictHeight();
16440             if(this.lastQuery == this.allQuery){
16441                 if(this.editable && !this.tickable){
16442                     this.inputEl().dom.select();
16443                 }
16444                 
16445                 if(
16446                     !this.selectByValue(this.value, true) &&
16447                     this.autoFocus && 
16448                     (
16449                         !this.store.lastOptions ||
16450                         typeof(this.store.lastOptions.add) == 'undefined' || 
16451                         this.store.lastOptions.add != true
16452                     )
16453                 ){
16454                     this.select(0, true);
16455                 }
16456             }else{
16457                 if(this.autoFocus){
16458                     this.selectNext();
16459                 }
16460                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16461                     this.taTask.delay(this.typeAheadDelay);
16462                 }
16463             }
16464         }else{
16465             this.onEmptyResults();
16466         }
16467         
16468         //this.el.focus();
16469     },
16470     // private
16471     onLoadException : function()
16472     {
16473         this.hasQuery = false;
16474         
16475         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16476             this.loading.hide();
16477         }
16478         
16479         if(this.tickable && this.editable){
16480             return;
16481         }
16482         
16483         this.collapse();
16484         // only causes errors at present
16485         //Roo.log(this.store.reader.jsonData);
16486         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16487             // fixme
16488             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16489         //}
16490         
16491         
16492     },
16493     // private
16494     onTypeAhead : function(){
16495         if(this.store.getCount() > 0){
16496             var r = this.store.getAt(0);
16497             var newValue = r.data[this.displayField];
16498             var len = newValue.length;
16499             var selStart = this.getRawValue().length;
16500             
16501             if(selStart != len){
16502                 this.setRawValue(newValue);
16503                 this.selectText(selStart, newValue.length);
16504             }
16505         }
16506     },
16507
16508     // private
16509     onSelect : function(record, index){
16510         
16511         if(this.fireEvent('beforeselect', this, record, index) !== false){
16512         
16513             this.setFromData(index > -1 ? record.data : false);
16514             
16515             this.collapse();
16516             this.fireEvent('select', this, record, index);
16517         }
16518     },
16519
16520     /**
16521      * Returns the currently selected field value or empty string if no value is set.
16522      * @return {String} value The selected value
16523      */
16524     getValue : function()
16525     {
16526         if(Roo.isIOS && this.useNativeIOS){
16527             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16528         }
16529         
16530         if(this.multiple){
16531             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16532         }
16533         
16534         if(this.valueField){
16535             return typeof this.value != 'undefined' ? this.value : '';
16536         }else{
16537             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16538         }
16539     },
16540     
16541     getRawValue : function()
16542     {
16543         if(Roo.isIOS && this.useNativeIOS){
16544             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16545         }
16546         
16547         var v = this.inputEl().getValue();
16548         
16549         return v;
16550     },
16551
16552     /**
16553      * Clears any text/value currently set in the field
16554      */
16555     clearValue : function(){
16556         
16557         if(this.hiddenField){
16558             this.hiddenField.dom.value = '';
16559         }
16560         this.value = '';
16561         this.setRawValue('');
16562         this.lastSelectionText = '';
16563         this.lastData = false;
16564         
16565         var close = this.closeTriggerEl();
16566         
16567         if(close){
16568             close.hide();
16569         }
16570         
16571         this.validate();
16572         
16573     },
16574
16575     /**
16576      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16577      * will be displayed in the field.  If the value does not match the data value of an existing item,
16578      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16579      * Otherwise the field will be blank (although the value will still be set).
16580      * @param {String} value The value to match
16581      */
16582     setValue : function(v)
16583     {
16584         if(Roo.isIOS && this.useNativeIOS){
16585             this.setIOSValue(v);
16586             return;
16587         }
16588         
16589         if(this.multiple){
16590             this.syncValue();
16591             return;
16592         }
16593         
16594         var text = v;
16595         if(this.valueField){
16596             var r = this.findRecord(this.valueField, v);
16597             if(r){
16598                 text = r.data[this.displayField];
16599             }else if(this.valueNotFoundText !== undefined){
16600                 text = this.valueNotFoundText;
16601             }
16602         }
16603         this.lastSelectionText = text;
16604         if(this.hiddenField){
16605             this.hiddenField.dom.value = v;
16606         }
16607         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16608         this.value = v;
16609         
16610         var close = this.closeTriggerEl();
16611         
16612         if(close){
16613             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16614         }
16615         
16616         this.validate();
16617     },
16618     /**
16619      * @property {Object} the last set data for the element
16620      */
16621     
16622     lastData : false,
16623     /**
16624      * Sets the value of the field based on a object which is related to the record format for the store.
16625      * @param {Object} value the value to set as. or false on reset?
16626      */
16627     setFromData : function(o){
16628         
16629         if(this.multiple){
16630             this.addItem(o);
16631             return;
16632         }
16633             
16634         var dv = ''; // display value
16635         var vv = ''; // value value..
16636         this.lastData = o;
16637         if (this.displayField) {
16638             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16639         } else {
16640             // this is an error condition!!!
16641             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16642         }
16643         
16644         if(this.valueField){
16645             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16646         }
16647         
16648         var close = this.closeTriggerEl();
16649         
16650         if(close){
16651             if(dv.length || vv * 1 > 0){
16652                 close.show() ;
16653                 this.blockFocus=true;
16654             } else {
16655                 close.hide();
16656             }             
16657         }
16658         
16659         if(this.hiddenField){
16660             this.hiddenField.dom.value = vv;
16661             
16662             this.lastSelectionText = dv;
16663             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16664             this.value = vv;
16665             return;
16666         }
16667         // no hidden field.. - we store the value in 'value', but still display
16668         // display field!!!!
16669         this.lastSelectionText = dv;
16670         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16671         this.value = vv;
16672         
16673         
16674         
16675     },
16676     // private
16677     reset : function(){
16678         // overridden so that last data is reset..
16679         
16680         if(this.multiple){
16681             this.clearItem();
16682             return;
16683         }
16684         
16685         this.setValue(this.originalValue);
16686         //this.clearInvalid();
16687         this.lastData = false;
16688         if (this.view) {
16689             this.view.clearSelections();
16690         }
16691         
16692         this.validate();
16693     },
16694     // private
16695     findRecord : function(prop, value){
16696         var record;
16697         if(this.store.getCount() > 0){
16698             this.store.each(function(r){
16699                 if(r.data[prop] == value){
16700                     record = r;
16701                     return false;
16702                 }
16703                 return true;
16704             });
16705         }
16706         return record;
16707     },
16708     
16709     getName: function()
16710     {
16711         // returns hidden if it's set..
16712         if (!this.rendered) {return ''};
16713         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16714         
16715     },
16716     // private
16717     onViewMove : function(e, t){
16718         this.inKeyMode = false;
16719     },
16720
16721     // private
16722     onViewOver : function(e, t){
16723         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16724             return;
16725         }
16726         var item = this.view.findItemFromChild(t);
16727         
16728         if(item){
16729             var index = this.view.indexOf(item);
16730             this.select(index, false);
16731         }
16732     },
16733
16734     // private
16735     onViewClick : function(view, doFocus, el, e)
16736     {
16737         var index = this.view.getSelectedIndexes()[0];
16738         
16739         var r = this.store.getAt(index);
16740         
16741         if(this.tickable){
16742             
16743             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16744                 return;
16745             }
16746             
16747             var rm = false;
16748             var _this = this;
16749             
16750             Roo.each(this.tickItems, function(v,k){
16751                 
16752                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16753                     Roo.log(v);
16754                     _this.tickItems.splice(k, 1);
16755                     
16756                     if(typeof(e) == 'undefined' && view == false){
16757                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16758                     }
16759                     
16760                     rm = true;
16761                     return;
16762                 }
16763             });
16764             
16765             if(rm){
16766                 return;
16767             }
16768             
16769             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16770                 this.tickItems.push(r.data);
16771             }
16772             
16773             if(typeof(e) == 'undefined' && view == false){
16774                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16775             }
16776                     
16777             return;
16778         }
16779         
16780         if(r){
16781             this.onSelect(r, index);
16782         }
16783         if(doFocus !== false && !this.blockFocus){
16784             this.inputEl().focus();
16785         }
16786     },
16787
16788     // private
16789     restrictHeight : function(){
16790         //this.innerList.dom.style.height = '';
16791         //var inner = this.innerList.dom;
16792         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16793         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16794         //this.list.beginUpdate();
16795         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16796         this.list.alignTo(this.inputEl(), this.listAlign);
16797         this.list.alignTo(this.inputEl(), this.listAlign);
16798         //this.list.endUpdate();
16799     },
16800
16801     // private
16802     onEmptyResults : function(){
16803         
16804         if(this.tickable && this.editable){
16805             this.hasFocus = false;
16806             this.restrictHeight();
16807             return;
16808         }
16809         
16810         this.collapse();
16811     },
16812
16813     /**
16814      * Returns true if the dropdown list is expanded, else false.
16815      */
16816     isExpanded : function(){
16817         return this.list.isVisible();
16818     },
16819
16820     /**
16821      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16822      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16823      * @param {String} value The data value of the item to select
16824      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16825      * selected item if it is not currently in view (defaults to true)
16826      * @return {Boolean} True if the value matched an item in the list, else false
16827      */
16828     selectByValue : function(v, scrollIntoView){
16829         if(v !== undefined && v !== null){
16830             var r = this.findRecord(this.valueField || this.displayField, v);
16831             if(r){
16832                 this.select(this.store.indexOf(r), scrollIntoView);
16833                 return true;
16834             }
16835         }
16836         return false;
16837     },
16838
16839     /**
16840      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16841      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16842      * @param {Number} index The zero-based index of the list item to select
16843      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16844      * selected item if it is not currently in view (defaults to true)
16845      */
16846     select : function(index, scrollIntoView){
16847         this.selectedIndex = index;
16848         this.view.select(index);
16849         if(scrollIntoView !== false){
16850             var el = this.view.getNode(index);
16851             /*
16852              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16853              */
16854             if(el){
16855                 this.list.scrollChildIntoView(el, false);
16856             }
16857         }
16858     },
16859
16860     // private
16861     selectNext : function(){
16862         var ct = this.store.getCount();
16863         if(ct > 0){
16864             if(this.selectedIndex == -1){
16865                 this.select(0);
16866             }else if(this.selectedIndex < ct-1){
16867                 this.select(this.selectedIndex+1);
16868             }
16869         }
16870     },
16871
16872     // private
16873     selectPrev : function(){
16874         var ct = this.store.getCount();
16875         if(ct > 0){
16876             if(this.selectedIndex == -1){
16877                 this.select(0);
16878             }else if(this.selectedIndex != 0){
16879                 this.select(this.selectedIndex-1);
16880             }
16881         }
16882     },
16883
16884     // private
16885     onKeyUp : function(e){
16886         if(this.editable !== false && !e.isSpecialKey()){
16887             this.lastKey = e.getKey();
16888             this.dqTask.delay(this.queryDelay);
16889         }
16890     },
16891
16892     // private
16893     validateBlur : function(){
16894         return !this.list || !this.list.isVisible();   
16895     },
16896
16897     // private
16898     initQuery : function(){
16899         
16900         var v = this.getRawValue();
16901         
16902         if(this.tickable && this.editable){
16903             v = this.tickableInputEl().getValue();
16904         }
16905         
16906         this.doQuery(v);
16907     },
16908
16909     // private
16910     doForce : function(){
16911         if(this.inputEl().dom.value.length > 0){
16912             this.inputEl().dom.value =
16913                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16914              
16915         }
16916     },
16917
16918     /**
16919      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16920      * query allowing the query action to be canceled if needed.
16921      * @param {String} query The SQL query to execute
16922      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16923      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16924      * saved in the current store (defaults to false)
16925      */
16926     doQuery : function(q, forceAll){
16927         
16928         if(q === undefined || q === null){
16929             q = '';
16930         }
16931         var qe = {
16932             query: q,
16933             forceAll: forceAll,
16934             combo: this,
16935             cancel:false
16936         };
16937         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16938             return false;
16939         }
16940         q = qe.query;
16941         
16942         forceAll = qe.forceAll;
16943         if(forceAll === true || (q.length >= this.minChars)){
16944             
16945             this.hasQuery = true;
16946             
16947             if(this.lastQuery != q || this.alwaysQuery){
16948                 this.lastQuery = q;
16949                 if(this.mode == 'local'){
16950                     this.selectedIndex = -1;
16951                     if(forceAll){
16952                         this.store.clearFilter();
16953                     }else{
16954                         
16955                         if(this.specialFilter){
16956                             this.fireEvent('specialfilter', this);
16957                             this.onLoad();
16958                             return;
16959                         }
16960                         
16961                         this.store.filter(this.displayField, q);
16962                     }
16963                     
16964                     this.store.fireEvent("datachanged", this.store);
16965                     
16966                     this.onLoad();
16967                     
16968                     
16969                 }else{
16970                     
16971                     this.store.baseParams[this.queryParam] = q;
16972                     
16973                     var options = {params : this.getParams(q)};
16974                     
16975                     if(this.loadNext){
16976                         options.add = true;
16977                         options.params.start = this.page * this.pageSize;
16978                     }
16979                     
16980                     this.store.load(options);
16981                     
16982                     /*
16983                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16984                      *  we should expand the list on onLoad
16985                      *  so command out it
16986                      */
16987 //                    this.expand();
16988                 }
16989             }else{
16990                 this.selectedIndex = -1;
16991                 this.onLoad();   
16992             }
16993         }
16994         
16995         this.loadNext = false;
16996     },
16997     
16998     // private
16999     getParams : function(q){
17000         var p = {};
17001         //p[this.queryParam] = q;
17002         
17003         if(this.pageSize){
17004             p.start = 0;
17005             p.limit = this.pageSize;
17006         }
17007         return p;
17008     },
17009
17010     /**
17011      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17012      */
17013     collapse : function(){
17014         if(!this.isExpanded()){
17015             return;
17016         }
17017         
17018         this.list.hide();
17019         
17020         this.hasFocus = false;
17021         
17022         if(this.tickable){
17023             this.okBtn.hide();
17024             this.cancelBtn.hide();
17025             this.trigger.show();
17026             
17027             if(this.editable){
17028                 this.tickableInputEl().dom.value = '';
17029                 this.tickableInputEl().blur();
17030             }
17031             
17032         }
17033         
17034         Roo.get(document).un('mousedown', this.collapseIf, this);
17035         Roo.get(document).un('mousewheel', this.collapseIf, this);
17036         if (!this.editable) {
17037             Roo.get(document).un('keydown', this.listKeyPress, this);
17038         }
17039         this.fireEvent('collapse', this);
17040         
17041         this.validate();
17042     },
17043
17044     // private
17045     collapseIf : function(e){
17046         var in_combo  = e.within(this.el);
17047         var in_list =  e.within(this.list);
17048         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17049         
17050         if (in_combo || in_list || is_list) {
17051             //e.stopPropagation();
17052             return;
17053         }
17054         
17055         if(this.tickable){
17056             this.onTickableFooterButtonClick(e, false, false);
17057         }
17058
17059         this.collapse();
17060         
17061     },
17062
17063     /**
17064      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17065      */
17066     expand : function(){
17067        
17068         if(this.isExpanded() || !this.hasFocus){
17069             return;
17070         }
17071         
17072         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17073         this.list.setWidth(lw);
17074         
17075         Roo.log('expand');
17076         
17077         this.list.show();
17078         
17079         this.restrictHeight();
17080         
17081         if(this.tickable){
17082             
17083             this.tickItems = Roo.apply([], this.item);
17084             
17085             this.okBtn.show();
17086             this.cancelBtn.show();
17087             this.trigger.hide();
17088             
17089             if(this.editable){
17090                 this.tickableInputEl().focus();
17091             }
17092             
17093         }
17094         
17095         Roo.get(document).on('mousedown', this.collapseIf, this);
17096         Roo.get(document).on('mousewheel', this.collapseIf, this);
17097         if (!this.editable) {
17098             Roo.get(document).on('keydown', this.listKeyPress, this);
17099         }
17100         
17101         this.fireEvent('expand', this);
17102     },
17103
17104     // private
17105     // Implements the default empty TriggerField.onTriggerClick function
17106     onTriggerClick : function(e)
17107     {
17108         Roo.log('trigger click');
17109         
17110         if(this.disabled || !this.triggerList){
17111             return;
17112         }
17113         
17114         this.page = 0;
17115         this.loadNext = false;
17116         
17117         if(this.isExpanded()){
17118             this.collapse();
17119             if (!this.blockFocus) {
17120                 this.inputEl().focus();
17121             }
17122             
17123         }else {
17124             this.hasFocus = true;
17125             if(this.triggerAction == 'all') {
17126                 this.doQuery(this.allQuery, true);
17127             } else {
17128                 this.doQuery(this.getRawValue());
17129             }
17130             if (!this.blockFocus) {
17131                 this.inputEl().focus();
17132             }
17133         }
17134     },
17135     
17136     onTickableTriggerClick : function(e)
17137     {
17138         if(this.disabled){
17139             return;
17140         }
17141         
17142         this.page = 0;
17143         this.loadNext = false;
17144         this.hasFocus = true;
17145         
17146         if(this.triggerAction == 'all') {
17147             this.doQuery(this.allQuery, true);
17148         } else {
17149             this.doQuery(this.getRawValue());
17150         }
17151     },
17152     
17153     onSearchFieldClick : function(e)
17154     {
17155         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17156             this.onTickableFooterButtonClick(e, false, false);
17157             return;
17158         }
17159         
17160         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17161             return;
17162         }
17163         
17164         this.page = 0;
17165         this.loadNext = false;
17166         this.hasFocus = true;
17167         
17168         if(this.triggerAction == 'all') {
17169             this.doQuery(this.allQuery, true);
17170         } else {
17171             this.doQuery(this.getRawValue());
17172         }
17173     },
17174     
17175     listKeyPress : function(e)
17176     {
17177         //Roo.log('listkeypress');
17178         // scroll to first matching element based on key pres..
17179         if (e.isSpecialKey()) {
17180             return false;
17181         }
17182         var k = String.fromCharCode(e.getKey()).toUpperCase();
17183         //Roo.log(k);
17184         var match  = false;
17185         var csel = this.view.getSelectedNodes();
17186         var cselitem = false;
17187         if (csel.length) {
17188             var ix = this.view.indexOf(csel[0]);
17189             cselitem  = this.store.getAt(ix);
17190             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17191                 cselitem = false;
17192             }
17193             
17194         }
17195         
17196         this.store.each(function(v) { 
17197             if (cselitem) {
17198                 // start at existing selection.
17199                 if (cselitem.id == v.id) {
17200                     cselitem = false;
17201                 }
17202                 return true;
17203             }
17204                 
17205             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17206                 match = this.store.indexOf(v);
17207                 return false;
17208             }
17209             return true;
17210         }, this);
17211         
17212         if (match === false) {
17213             return true; // no more action?
17214         }
17215         // scroll to?
17216         this.view.select(match);
17217         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17218         sn.scrollIntoView(sn.dom.parentNode, false);
17219     },
17220     
17221     onViewScroll : function(e, t){
17222         
17223         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){
17224             return;
17225         }
17226         
17227         this.hasQuery = true;
17228         
17229         this.loading = this.list.select('.loading', true).first();
17230         
17231         if(this.loading === null){
17232             this.list.createChild({
17233                 tag: 'div',
17234                 cls: 'loading roo-select2-more-results roo-select2-active',
17235                 html: 'Loading more results...'
17236             });
17237             
17238             this.loading = this.list.select('.loading', true).first();
17239             
17240             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17241             
17242             this.loading.hide();
17243         }
17244         
17245         this.loading.show();
17246         
17247         var _combo = this;
17248         
17249         this.page++;
17250         this.loadNext = true;
17251         
17252         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17253         
17254         return;
17255     },
17256     
17257     addItem : function(o)
17258     {   
17259         var dv = ''; // display value
17260         
17261         if (this.displayField) {
17262             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17263         } else {
17264             // this is an error condition!!!
17265             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17266         }
17267         
17268         if(!dv.length){
17269             return;
17270         }
17271         
17272         var choice = this.choices.createChild({
17273             tag: 'li',
17274             cls: 'roo-select2-search-choice',
17275             cn: [
17276                 {
17277                     tag: 'div',
17278                     html: dv
17279                 },
17280                 {
17281                     tag: 'a',
17282                     href: '#',
17283                     cls: 'roo-select2-search-choice-close fa fa-times',
17284                     tabindex: '-1'
17285                 }
17286             ]
17287             
17288         }, this.searchField);
17289         
17290         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17291         
17292         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17293         
17294         this.item.push(o);
17295         
17296         this.lastData = o;
17297         
17298         this.syncValue();
17299         
17300         this.inputEl().dom.value = '';
17301         
17302         this.validate();
17303     },
17304     
17305     onRemoveItem : function(e, _self, o)
17306     {
17307         e.preventDefault();
17308         
17309         this.lastItem = Roo.apply([], this.item);
17310         
17311         var index = this.item.indexOf(o.data) * 1;
17312         
17313         if( index < 0){
17314             Roo.log('not this item?!');
17315             return;
17316         }
17317         
17318         this.item.splice(index, 1);
17319         o.item.remove();
17320         
17321         this.syncValue();
17322         
17323         this.fireEvent('remove', this, e);
17324         
17325         this.validate();
17326         
17327     },
17328     
17329     syncValue : function()
17330     {
17331         if(!this.item.length){
17332             this.clearValue();
17333             return;
17334         }
17335             
17336         var value = [];
17337         var _this = this;
17338         Roo.each(this.item, function(i){
17339             if(_this.valueField){
17340                 value.push(i[_this.valueField]);
17341                 return;
17342             }
17343
17344             value.push(i);
17345         });
17346
17347         this.value = value.join(',');
17348
17349         if(this.hiddenField){
17350             this.hiddenField.dom.value = this.value;
17351         }
17352         
17353         this.store.fireEvent("datachanged", this.store);
17354         
17355         this.validate();
17356     },
17357     
17358     clearItem : function()
17359     {
17360         if(!this.multiple){
17361             return;
17362         }
17363         
17364         this.item = [];
17365         
17366         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17367            c.remove();
17368         });
17369         
17370         this.syncValue();
17371         
17372         this.validate();
17373         
17374         if(this.tickable && !Roo.isTouch){
17375             this.view.refresh();
17376         }
17377     },
17378     
17379     inputEl: function ()
17380     {
17381         if(Roo.isIOS && this.useNativeIOS){
17382             return this.el.select('select.roo-ios-select', true).first();
17383         }
17384         
17385         if(Roo.isTouch && this.mobileTouchView){
17386             return this.el.select('input.form-control',true).first();
17387         }
17388         
17389         if(this.tickable){
17390             return this.searchField;
17391         }
17392         
17393         return this.el.select('input.form-control',true).first();
17394     },
17395     
17396     onTickableFooterButtonClick : function(e, btn, el)
17397     {
17398         e.preventDefault();
17399         
17400         this.lastItem = Roo.apply([], this.item);
17401         
17402         if(btn && btn.name == 'cancel'){
17403             this.tickItems = Roo.apply([], this.item);
17404             this.collapse();
17405             return;
17406         }
17407         
17408         this.clearItem();
17409         
17410         var _this = this;
17411         
17412         Roo.each(this.tickItems, function(o){
17413             _this.addItem(o);
17414         });
17415         
17416         this.collapse();
17417         
17418     },
17419     
17420     validate : function()
17421     {
17422         if(this.getVisibilityEl().hasClass('hidden')){
17423             return true;
17424         }
17425         
17426         var v = this.getRawValue();
17427         
17428         if(this.multiple){
17429             v = this.getValue();
17430         }
17431         
17432         if(this.disabled || this.allowBlank || v.length){
17433             this.markValid();
17434             return true;
17435         }
17436         
17437         this.markInvalid();
17438         return false;
17439     },
17440     
17441     tickableInputEl : function()
17442     {
17443         if(!this.tickable || !this.editable){
17444             return this.inputEl();
17445         }
17446         
17447         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17448     },
17449     
17450     
17451     getAutoCreateTouchView : function()
17452     {
17453         var id = Roo.id();
17454         
17455         var cfg = {
17456             cls: 'form-group' //input-group
17457         };
17458         
17459         var input =  {
17460             tag: 'input',
17461             id : id,
17462             type : this.inputType,
17463             cls : 'form-control x-combo-noedit',
17464             autocomplete: 'new-password',
17465             placeholder : this.placeholder || '',
17466             readonly : true
17467         };
17468         
17469         if (this.name) {
17470             input.name = this.name;
17471         }
17472         
17473         if (this.size) {
17474             input.cls += ' input-' + this.size;
17475         }
17476         
17477         if (this.disabled) {
17478             input.disabled = true;
17479         }
17480         
17481         var inputblock = {
17482             cls : 'roo-combobox-wrap',
17483             cn : [
17484                 input
17485             ]
17486         };
17487         
17488         if(this.before){
17489             inputblock.cls += ' input-group';
17490             
17491             inputblock.cn.unshift({
17492                 tag :'span',
17493                 cls : 'input-group-addon input-group-prepend input-group-text',
17494                 html : this.before
17495             });
17496         }
17497         
17498         if(this.removable && !this.multiple){
17499             inputblock.cls += ' roo-removable';
17500             
17501             inputblock.cn.push({
17502                 tag: 'button',
17503                 html : 'x',
17504                 cls : 'roo-combo-removable-btn close'
17505             });
17506         }
17507
17508         if(this.hasFeedback && !this.allowBlank){
17509             
17510             inputblock.cls += ' has-feedback';
17511             
17512             inputblock.cn.push({
17513                 tag: 'span',
17514                 cls: 'glyphicon form-control-feedback'
17515             });
17516             
17517         }
17518         
17519         if (this.after) {
17520             
17521             inputblock.cls += (this.before) ? '' : ' input-group';
17522             
17523             inputblock.cn.push({
17524                 tag :'span',
17525                 cls : 'input-group-addon input-group-append input-group-text',
17526                 html : this.after
17527             });
17528         }
17529
17530         
17531         var ibwrap = inputblock;
17532         
17533         if(this.multiple){
17534             ibwrap = {
17535                 tag: 'ul',
17536                 cls: 'roo-select2-choices',
17537                 cn:[
17538                     {
17539                         tag: 'li',
17540                         cls: 'roo-select2-search-field',
17541                         cn: [
17542
17543                             inputblock
17544                         ]
17545                     }
17546                 ]
17547             };
17548         
17549             
17550         }
17551         
17552         var combobox = {
17553             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17554             cn: [
17555                 {
17556                     tag: 'input',
17557                     type : 'hidden',
17558                     cls: 'form-hidden-field'
17559                 },
17560                 ibwrap
17561             ]
17562         };
17563         
17564         if(!this.multiple && this.showToggleBtn){
17565             
17566             var caret = {
17567                 cls: 'caret'
17568             };
17569             
17570             if (this.caret != false) {
17571                 caret = {
17572                      tag: 'i',
17573                      cls: 'fa fa-' + this.caret
17574                 };
17575                 
17576             }
17577             
17578             combobox.cn.push({
17579                 tag :'span',
17580                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17581                 cn : [
17582                     Roo.bootstrap.version == 3 ? caret : '',
17583                     {
17584                         tag: 'span',
17585                         cls: 'combobox-clear',
17586                         cn  : [
17587                             {
17588                                 tag : 'i',
17589                                 cls: 'icon-remove'
17590                             }
17591                         ]
17592                     }
17593                 ]
17594
17595             })
17596         }
17597         
17598         if(this.multiple){
17599             combobox.cls += ' roo-select2-container-multi';
17600         }
17601         
17602         var required =  this.allowBlank ?  {
17603                     tag : 'i',
17604                     style: 'display: none'
17605                 } : {
17606                    tag : 'i',
17607                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17608                    tooltip : 'This field is required'
17609                 };
17610         
17611         var align = this.labelAlign || this.parentLabelAlign();
17612         
17613         if (align ==='left' && this.fieldLabel.length) {
17614
17615             cfg.cn = [
17616                 required,
17617                 {
17618                     tag: 'label',
17619                     cls : 'control-label col-form-label',
17620                     html : this.fieldLabel
17621
17622                 },
17623                 {
17624                     cls : 'roo-combobox-wrap ', 
17625                     cn: [
17626                         combobox
17627                     ]
17628                 }
17629             ];
17630             
17631             var labelCfg = cfg.cn[1];
17632             var contentCfg = cfg.cn[2];
17633             
17634
17635             if(this.indicatorpos == 'right'){
17636                 cfg.cn = [
17637                     {
17638                         tag: 'label',
17639                         'for' :  id,
17640                         cls : 'control-label col-form-label',
17641                         cn : [
17642                             {
17643                                 tag : 'span',
17644                                 html : this.fieldLabel
17645                             },
17646                             required
17647                         ]
17648                     },
17649                     {
17650                         cls : "roo-combobox-wrap ",
17651                         cn: [
17652                             combobox
17653                         ]
17654                     }
17655
17656                 ];
17657                 
17658                 labelCfg = cfg.cn[0];
17659                 contentCfg = cfg.cn[1];
17660             }
17661             
17662            
17663             
17664             if(this.labelWidth > 12){
17665                 labelCfg.style = "width: " + this.labelWidth + 'px';
17666             }
17667            
17668             if(this.labelWidth < 13 && this.labelmd == 0){
17669                 this.labelmd = this.labelWidth;
17670             }
17671             
17672             if(this.labellg > 0){
17673                 labelCfg.cls += ' col-lg-' + this.labellg;
17674                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17675             }
17676             
17677             if(this.labelmd > 0){
17678                 labelCfg.cls += ' col-md-' + this.labelmd;
17679                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17680             }
17681             
17682             if(this.labelsm > 0){
17683                 labelCfg.cls += ' col-sm-' + this.labelsm;
17684                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17685             }
17686             
17687             if(this.labelxs > 0){
17688                 labelCfg.cls += ' col-xs-' + this.labelxs;
17689                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17690             }
17691                 
17692                 
17693         } else if ( this.fieldLabel.length) {
17694             cfg.cn = [
17695                required,
17696                 {
17697                     tag: 'label',
17698                     cls : 'control-label',
17699                     html : this.fieldLabel
17700
17701                 },
17702                 {
17703                     cls : '', 
17704                     cn: [
17705                         combobox
17706                     ]
17707                 }
17708             ];
17709             
17710             if(this.indicatorpos == 'right'){
17711                 cfg.cn = [
17712                     {
17713                         tag: 'label',
17714                         cls : 'control-label',
17715                         html : this.fieldLabel,
17716                         cn : [
17717                             required
17718                         ]
17719                     },
17720                     {
17721                         cls : '', 
17722                         cn: [
17723                             combobox
17724                         ]
17725                     }
17726                 ];
17727             }
17728         } else {
17729             cfg.cn = combobox;    
17730         }
17731         
17732         
17733         var settings = this;
17734         
17735         ['xs','sm','md','lg'].map(function(size){
17736             if (settings[size]) {
17737                 cfg.cls += ' col-' + size + '-' + settings[size];
17738             }
17739         });
17740         
17741         return cfg;
17742     },
17743     
17744     initTouchView : function()
17745     {
17746         this.renderTouchView();
17747         
17748         this.touchViewEl.on('scroll', function(){
17749             this.el.dom.scrollTop = 0;
17750         }, this);
17751         
17752         this.originalValue = this.getValue();
17753         
17754         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17755         
17756         this.inputEl().on("click", this.showTouchView, this);
17757         if (this.triggerEl) {
17758             this.triggerEl.on("click", this.showTouchView, this);
17759         }
17760         
17761         
17762         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17763         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17764         
17765         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17766         
17767         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17768         this.store.on('load', this.onTouchViewLoad, this);
17769         this.store.on('loadexception', this.onTouchViewLoadException, this);
17770         
17771         if(this.hiddenName){
17772             
17773             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17774             
17775             this.hiddenField.dom.value =
17776                 this.hiddenValue !== undefined ? this.hiddenValue :
17777                 this.value !== undefined ? this.value : '';
17778         
17779             this.el.dom.removeAttribute('name');
17780             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17781         }
17782         
17783         if(this.multiple){
17784             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17785             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17786         }
17787         
17788         if(this.removable && !this.multiple){
17789             var close = this.closeTriggerEl();
17790             if(close){
17791                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17792                 close.on('click', this.removeBtnClick, this, close);
17793             }
17794         }
17795         /*
17796          * fix the bug in Safari iOS8
17797          */
17798         this.inputEl().on("focus", function(e){
17799             document.activeElement.blur();
17800         }, this);
17801         
17802         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17803         
17804         return;
17805         
17806         
17807     },
17808     
17809     renderTouchView : function()
17810     {
17811         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17812         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17813         
17814         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17815         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17816         
17817         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17818         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17819         this.touchViewBodyEl.setStyle('overflow', 'auto');
17820         
17821         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17822         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17823         
17824         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17825         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17826         
17827     },
17828     
17829     showTouchView : function()
17830     {
17831         if(this.disabled){
17832             return;
17833         }
17834         
17835         this.touchViewHeaderEl.hide();
17836
17837         if(this.modalTitle.length){
17838             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17839             this.touchViewHeaderEl.show();
17840         }
17841
17842         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17843         this.touchViewEl.show();
17844
17845         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17846         
17847         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17848         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17849
17850         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17851
17852         if(this.modalTitle.length){
17853             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17854         }
17855         
17856         this.touchViewBodyEl.setHeight(bodyHeight);
17857
17858         if(this.animate){
17859             var _this = this;
17860             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17861         }else{
17862             this.touchViewEl.addClass(['in','show']);
17863         }
17864         
17865         if(this._touchViewMask){
17866             Roo.get(document.body).addClass("x-body-masked");
17867             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17868             this._touchViewMask.setStyle('z-index', 10000);
17869             this._touchViewMask.addClass('show');
17870         }
17871         
17872         this.doTouchViewQuery();
17873         
17874     },
17875     
17876     hideTouchView : function()
17877     {
17878         this.touchViewEl.removeClass(['in','show']);
17879
17880         if(this.animate){
17881             var _this = this;
17882             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17883         }else{
17884             this.touchViewEl.setStyle('display', 'none');
17885         }
17886         
17887         if(this._touchViewMask){
17888             this._touchViewMask.removeClass('show');
17889             Roo.get(document.body).removeClass("x-body-masked");
17890         }
17891     },
17892     
17893     setTouchViewValue : function()
17894     {
17895         if(this.multiple){
17896             this.clearItem();
17897         
17898             var _this = this;
17899
17900             Roo.each(this.tickItems, function(o){
17901                 this.addItem(o);
17902             }, this);
17903         }
17904         
17905         this.hideTouchView();
17906     },
17907     
17908     doTouchViewQuery : function()
17909     {
17910         var qe = {
17911             query: '',
17912             forceAll: true,
17913             combo: this,
17914             cancel:false
17915         };
17916         
17917         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17918             return false;
17919         }
17920         
17921         if(!this.alwaysQuery || this.mode == 'local'){
17922             this.onTouchViewLoad();
17923             return;
17924         }
17925         
17926         this.store.load();
17927     },
17928     
17929     onTouchViewBeforeLoad : function(combo,opts)
17930     {
17931         return;
17932     },
17933
17934     // private
17935     onTouchViewLoad : function()
17936     {
17937         if(this.store.getCount() < 1){
17938             this.onTouchViewEmptyResults();
17939             return;
17940         }
17941         
17942         this.clearTouchView();
17943         
17944         var rawValue = this.getRawValue();
17945         
17946         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17947         
17948         this.tickItems = [];
17949         
17950         this.store.data.each(function(d, rowIndex){
17951             var row = this.touchViewListGroup.createChild(template);
17952             
17953             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17954                 row.addClass(d.data.cls);
17955             }
17956             
17957             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17958                 var cfg = {
17959                     data : d.data,
17960                     html : d.data[this.displayField]
17961                 };
17962                 
17963                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17964                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17965                 }
17966             }
17967             row.removeClass('selected');
17968             if(!this.multiple && this.valueField &&
17969                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17970             {
17971                 // radio buttons..
17972                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17973                 row.addClass('selected');
17974             }
17975             
17976             if(this.multiple && this.valueField &&
17977                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17978             {
17979                 
17980                 // checkboxes...
17981                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17982                 this.tickItems.push(d.data);
17983             }
17984             
17985             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17986             
17987         }, this);
17988         
17989         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17990         
17991         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17992
17993         if(this.modalTitle.length){
17994             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17995         }
17996
17997         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17998         
17999         if(this.mobile_restrict_height && listHeight < bodyHeight){
18000             this.touchViewBodyEl.setHeight(listHeight);
18001         }
18002         
18003         var _this = this;
18004         
18005         if(firstChecked && listHeight > bodyHeight){
18006             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18007         }
18008         
18009     },
18010     
18011     onTouchViewLoadException : function()
18012     {
18013         this.hideTouchView();
18014     },
18015     
18016     onTouchViewEmptyResults : function()
18017     {
18018         this.clearTouchView();
18019         
18020         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18021         
18022         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18023         
18024     },
18025     
18026     clearTouchView : function()
18027     {
18028         this.touchViewListGroup.dom.innerHTML = '';
18029     },
18030     
18031     onTouchViewClick : function(e, el, o)
18032     {
18033         e.preventDefault();
18034         
18035         var row = o.row;
18036         var rowIndex = o.rowIndex;
18037         
18038         var r = this.store.getAt(rowIndex);
18039         
18040         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18041             
18042             if(!this.multiple){
18043                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18044                     c.dom.removeAttribute('checked');
18045                 }, this);
18046
18047                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18048
18049                 this.setFromData(r.data);
18050
18051                 var close = this.closeTriggerEl();
18052
18053                 if(close){
18054                     close.show();
18055                 }
18056
18057                 this.hideTouchView();
18058
18059                 this.fireEvent('select', this, r, rowIndex);
18060
18061                 return;
18062             }
18063
18064             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18065                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18066                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18067                 return;
18068             }
18069
18070             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18071             this.addItem(r.data);
18072             this.tickItems.push(r.data);
18073         }
18074     },
18075     
18076     getAutoCreateNativeIOS : function()
18077     {
18078         var cfg = {
18079             cls: 'form-group' //input-group,
18080         };
18081         
18082         var combobox =  {
18083             tag: 'select',
18084             cls : 'roo-ios-select'
18085         };
18086         
18087         if (this.name) {
18088             combobox.name = this.name;
18089         }
18090         
18091         if (this.disabled) {
18092             combobox.disabled = true;
18093         }
18094         
18095         var settings = this;
18096         
18097         ['xs','sm','md','lg'].map(function(size){
18098             if (settings[size]) {
18099                 cfg.cls += ' col-' + size + '-' + settings[size];
18100             }
18101         });
18102         
18103         cfg.cn = combobox;
18104         
18105         return cfg;
18106         
18107     },
18108     
18109     initIOSView : function()
18110     {
18111         this.store.on('load', this.onIOSViewLoad, this);
18112         
18113         return;
18114     },
18115     
18116     onIOSViewLoad : function()
18117     {
18118         if(this.store.getCount() < 1){
18119             return;
18120         }
18121         
18122         this.clearIOSView();
18123         
18124         if(this.allowBlank) {
18125             
18126             var default_text = '-- SELECT --';
18127             
18128             if(this.placeholder.length){
18129                 default_text = this.placeholder;
18130             }
18131             
18132             if(this.emptyTitle.length){
18133                 default_text += ' - ' + this.emptyTitle + ' -';
18134             }
18135             
18136             var opt = this.inputEl().createChild({
18137                 tag: 'option',
18138                 value : 0,
18139                 html : default_text
18140             });
18141             
18142             var o = {};
18143             o[this.valueField] = 0;
18144             o[this.displayField] = default_text;
18145             
18146             this.ios_options.push({
18147                 data : o,
18148                 el : opt
18149             });
18150             
18151         }
18152         
18153         this.store.data.each(function(d, rowIndex){
18154             
18155             var html = '';
18156             
18157             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18158                 html = d.data[this.displayField];
18159             }
18160             
18161             var value = '';
18162             
18163             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18164                 value = d.data[this.valueField];
18165             }
18166             
18167             var option = {
18168                 tag: 'option',
18169                 value : value,
18170                 html : html
18171             };
18172             
18173             if(this.value == d.data[this.valueField]){
18174                 option['selected'] = true;
18175             }
18176             
18177             var opt = this.inputEl().createChild(option);
18178             
18179             this.ios_options.push({
18180                 data : d.data,
18181                 el : opt
18182             });
18183             
18184         }, this);
18185         
18186         this.inputEl().on('change', function(){
18187            this.fireEvent('select', this);
18188         }, this);
18189         
18190     },
18191     
18192     clearIOSView: function()
18193     {
18194         this.inputEl().dom.innerHTML = '';
18195         
18196         this.ios_options = [];
18197     },
18198     
18199     setIOSValue: function(v)
18200     {
18201         this.value = v;
18202         
18203         if(!this.ios_options){
18204             return;
18205         }
18206         
18207         Roo.each(this.ios_options, function(opts){
18208            
18209            opts.el.dom.removeAttribute('selected');
18210            
18211            if(opts.data[this.valueField] != v){
18212                return;
18213            }
18214            
18215            opts.el.dom.setAttribute('selected', true);
18216            
18217         }, this);
18218     }
18219
18220     /** 
18221     * @cfg {Boolean} grow 
18222     * @hide 
18223     */
18224     /** 
18225     * @cfg {Number} growMin 
18226     * @hide 
18227     */
18228     /** 
18229     * @cfg {Number} growMax 
18230     * @hide 
18231     */
18232     /**
18233      * @hide
18234      * @method autoSize
18235      */
18236 });
18237
18238 Roo.apply(Roo.bootstrap.ComboBox,  {
18239     
18240     header : {
18241         tag: 'div',
18242         cls: 'modal-header',
18243         cn: [
18244             {
18245                 tag: 'h4',
18246                 cls: 'modal-title'
18247             }
18248         ]
18249     },
18250     
18251     body : {
18252         tag: 'div',
18253         cls: 'modal-body',
18254         cn: [
18255             {
18256                 tag: 'ul',
18257                 cls: 'list-group'
18258             }
18259         ]
18260     },
18261     
18262     listItemRadio : {
18263         tag: 'li',
18264         cls: 'list-group-item',
18265         cn: [
18266             {
18267                 tag: 'span',
18268                 cls: 'roo-combobox-list-group-item-value'
18269             },
18270             {
18271                 tag: 'div',
18272                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18273                 cn: [
18274                     {
18275                         tag: 'input',
18276                         type: 'radio'
18277                     },
18278                     {
18279                         tag: 'label'
18280                     }
18281                 ]
18282             }
18283         ]
18284     },
18285     
18286     listItemCheckbox : {
18287         tag: 'li',
18288         cls: 'list-group-item',
18289         cn: [
18290             {
18291                 tag: 'span',
18292                 cls: 'roo-combobox-list-group-item-value'
18293             },
18294             {
18295                 tag: 'div',
18296                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18297                 cn: [
18298                     {
18299                         tag: 'input',
18300                         type: 'checkbox'
18301                     },
18302                     {
18303                         tag: 'label'
18304                     }
18305                 ]
18306             }
18307         ]
18308     },
18309     
18310     emptyResult : {
18311         tag: 'div',
18312         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18313     },
18314     
18315     footer : {
18316         tag: 'div',
18317         cls: 'modal-footer',
18318         cn: [
18319             {
18320                 tag: 'div',
18321                 cls: 'row',
18322                 cn: [
18323                     {
18324                         tag: 'div',
18325                         cls: 'col-xs-6 text-left',
18326                         cn: {
18327                             tag: 'button',
18328                             cls: 'btn btn-danger roo-touch-view-cancel',
18329                             html: 'Cancel'
18330                         }
18331                     },
18332                     {
18333                         tag: 'div',
18334                         cls: 'col-xs-6 text-right',
18335                         cn: {
18336                             tag: 'button',
18337                             cls: 'btn btn-success roo-touch-view-ok',
18338                             html: 'OK'
18339                         }
18340                     }
18341                 ]
18342             }
18343         ]
18344         
18345     }
18346 });
18347
18348 Roo.apply(Roo.bootstrap.ComboBox,  {
18349     
18350     touchViewTemplate : {
18351         tag: 'div',
18352         cls: 'modal fade roo-combobox-touch-view',
18353         cn: [
18354             {
18355                 tag: 'div',
18356                 cls: 'modal-dialog',
18357                 style : 'position:fixed', // we have to fix position....
18358                 cn: [
18359                     {
18360                         tag: 'div',
18361                         cls: 'modal-content',
18362                         cn: [
18363                             Roo.bootstrap.ComboBox.header,
18364                             Roo.bootstrap.ComboBox.body,
18365                             Roo.bootstrap.ComboBox.footer
18366                         ]
18367                     }
18368                 ]
18369             }
18370         ]
18371     }
18372 });/*
18373  * Based on:
18374  * Ext JS Library 1.1.1
18375  * Copyright(c) 2006-2007, Ext JS, LLC.
18376  *
18377  * Originally Released Under LGPL - original licence link has changed is not relivant.
18378  *
18379  * Fork - LGPL
18380  * <script type="text/javascript">
18381  */
18382
18383 /**
18384  * @class Roo.View
18385  * @extends Roo.util.Observable
18386  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18387  * This class also supports single and multi selection modes. <br>
18388  * Create a data model bound view:
18389  <pre><code>
18390  var store = new Roo.data.Store(...);
18391
18392  var view = new Roo.View({
18393     el : "my-element",
18394     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18395  
18396     singleSelect: true,
18397     selectedClass: "ydataview-selected",
18398     store: store
18399  });
18400
18401  // listen for node click?
18402  view.on("click", function(vw, index, node, e){
18403  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18404  });
18405
18406  // load XML data
18407  dataModel.load("foobar.xml");
18408  </code></pre>
18409  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18410  * <br><br>
18411  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18412  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18413  * 
18414  * Note: old style constructor is still suported (container, template, config)
18415  * 
18416  * @constructor
18417  * Create a new View
18418  * @param {Object} config The config object
18419  * 
18420  */
18421 Roo.View = function(config, depreciated_tpl, depreciated_config){
18422     
18423     this.parent = false;
18424     
18425     if (typeof(depreciated_tpl) == 'undefined') {
18426         // new way.. - universal constructor.
18427         Roo.apply(this, config);
18428         this.el  = Roo.get(this.el);
18429     } else {
18430         // old format..
18431         this.el  = Roo.get(config);
18432         this.tpl = depreciated_tpl;
18433         Roo.apply(this, depreciated_config);
18434     }
18435     this.wrapEl  = this.el.wrap().wrap();
18436     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18437     
18438     
18439     if(typeof(this.tpl) == "string"){
18440         this.tpl = new Roo.Template(this.tpl);
18441     } else {
18442         // support xtype ctors..
18443         this.tpl = new Roo.factory(this.tpl, Roo);
18444     }
18445     
18446     
18447     this.tpl.compile();
18448     
18449     /** @private */
18450     this.addEvents({
18451         /**
18452          * @event beforeclick
18453          * Fires before a click is processed. Returns false to cancel the default action.
18454          * @param {Roo.View} this
18455          * @param {Number} index The index of the target node
18456          * @param {HTMLElement} node The target node
18457          * @param {Roo.EventObject} e The raw event object
18458          */
18459             "beforeclick" : true,
18460         /**
18461          * @event click
18462          * Fires when a template node is clicked.
18463          * @param {Roo.View} this
18464          * @param {Number} index The index of the target node
18465          * @param {HTMLElement} node The target node
18466          * @param {Roo.EventObject} e The raw event object
18467          */
18468             "click" : true,
18469         /**
18470          * @event dblclick
18471          * Fires when a template node is double clicked.
18472          * @param {Roo.View} this
18473          * @param {Number} index The index of the target node
18474          * @param {HTMLElement} node The target node
18475          * @param {Roo.EventObject} e The raw event object
18476          */
18477             "dblclick" : true,
18478         /**
18479          * @event contextmenu
18480          * Fires when a template node is right clicked.
18481          * @param {Roo.View} this
18482          * @param {Number} index The index of the target node
18483          * @param {HTMLElement} node The target node
18484          * @param {Roo.EventObject} e The raw event object
18485          */
18486             "contextmenu" : true,
18487         /**
18488          * @event selectionchange
18489          * Fires when the selected nodes change.
18490          * @param {Roo.View} this
18491          * @param {Array} selections Array of the selected nodes
18492          */
18493             "selectionchange" : true,
18494     
18495         /**
18496          * @event beforeselect
18497          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18498          * @param {Roo.View} this
18499          * @param {HTMLElement} node The node to be selected
18500          * @param {Array} selections Array of currently selected nodes
18501          */
18502             "beforeselect" : true,
18503         /**
18504          * @event preparedata
18505          * Fires on every row to render, to allow you to change the data.
18506          * @param {Roo.View} this
18507          * @param {Object} data to be rendered (change this)
18508          */
18509           "preparedata" : true
18510           
18511           
18512         });
18513
18514
18515
18516     this.el.on({
18517         "click": this.onClick,
18518         "dblclick": this.onDblClick,
18519         "contextmenu": this.onContextMenu,
18520         scope:this
18521     });
18522
18523     this.selections = [];
18524     this.nodes = [];
18525     this.cmp = new Roo.CompositeElementLite([]);
18526     if(this.store){
18527         this.store = Roo.factory(this.store, Roo.data);
18528         this.setStore(this.store, true);
18529     }
18530     
18531     if ( this.footer && this.footer.xtype) {
18532            
18533          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18534         
18535         this.footer.dataSource = this.store;
18536         this.footer.container = fctr;
18537         this.footer = Roo.factory(this.footer, Roo);
18538         fctr.insertFirst(this.el);
18539         
18540         // this is a bit insane - as the paging toolbar seems to detach the el..
18541 //        dom.parentNode.parentNode.parentNode
18542          // they get detached?
18543     }
18544     
18545     
18546     Roo.View.superclass.constructor.call(this);
18547     
18548     
18549 };
18550
18551 Roo.extend(Roo.View, Roo.util.Observable, {
18552     
18553      /**
18554      * @cfg {Roo.data.Store} store Data store to load data from.
18555      */
18556     store : false,
18557     
18558     /**
18559      * @cfg {String|Roo.Element} el The container element.
18560      */
18561     el : '',
18562     
18563     /**
18564      * @cfg {String|Roo.Template} tpl The template used by this View 
18565      */
18566     tpl : false,
18567     /**
18568      * @cfg {String} dataName the named area of the template to use as the data area
18569      *                          Works with domtemplates roo-name="name"
18570      */
18571     dataName: false,
18572     /**
18573      * @cfg {String} selectedClass The css class to add to selected nodes
18574      */
18575     selectedClass : "x-view-selected",
18576      /**
18577      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18578      */
18579     emptyText : "",
18580     
18581     /**
18582      * @cfg {String} text to display on mask (default Loading)
18583      */
18584     mask : false,
18585     /**
18586      * @cfg {Boolean} multiSelect Allow multiple selection
18587      */
18588     multiSelect : false,
18589     /**
18590      * @cfg {Boolean} singleSelect Allow single selection
18591      */
18592     singleSelect:  false,
18593     
18594     /**
18595      * @cfg {Boolean} toggleSelect - selecting 
18596      */
18597     toggleSelect : false,
18598     
18599     /**
18600      * @cfg {Boolean} tickable - selecting 
18601      */
18602     tickable : false,
18603     
18604     /**
18605      * Returns the element this view is bound to.
18606      * @return {Roo.Element}
18607      */
18608     getEl : function(){
18609         return this.wrapEl;
18610     },
18611     
18612     
18613
18614     /**
18615      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18616      */
18617     refresh : function(){
18618         //Roo.log('refresh');
18619         var t = this.tpl;
18620         
18621         // if we are using something like 'domtemplate', then
18622         // the what gets used is:
18623         // t.applySubtemplate(NAME, data, wrapping data..)
18624         // the outer template then get' applied with
18625         //     the store 'extra data'
18626         // and the body get's added to the
18627         //      roo-name="data" node?
18628         //      <span class='roo-tpl-{name}'></span> ?????
18629         
18630         
18631         
18632         this.clearSelections();
18633         this.el.update("");
18634         var html = [];
18635         var records = this.store.getRange();
18636         if(records.length < 1) {
18637             
18638             // is this valid??  = should it render a template??
18639             
18640             this.el.update(this.emptyText);
18641             return;
18642         }
18643         var el = this.el;
18644         if (this.dataName) {
18645             this.el.update(t.apply(this.store.meta)); //????
18646             el = this.el.child('.roo-tpl-' + this.dataName);
18647         }
18648         
18649         for(var i = 0, len = records.length; i < len; i++){
18650             var data = this.prepareData(records[i].data, i, records[i]);
18651             this.fireEvent("preparedata", this, data, i, records[i]);
18652             
18653             var d = Roo.apply({}, data);
18654             
18655             if(this.tickable){
18656                 Roo.apply(d, {'roo-id' : Roo.id()});
18657                 
18658                 var _this = this;
18659             
18660                 Roo.each(this.parent.item, function(item){
18661                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18662                         return;
18663                     }
18664                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18665                 });
18666             }
18667             
18668             html[html.length] = Roo.util.Format.trim(
18669                 this.dataName ?
18670                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18671                     t.apply(d)
18672             );
18673         }
18674         
18675         
18676         
18677         el.update(html.join(""));
18678         this.nodes = el.dom.childNodes;
18679         this.updateIndexes(0);
18680     },
18681     
18682
18683     /**
18684      * Function to override to reformat the data that is sent to
18685      * the template for each node.
18686      * DEPRICATED - use the preparedata event handler.
18687      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18688      * a JSON object for an UpdateManager bound view).
18689      */
18690     prepareData : function(data, index, record)
18691     {
18692         this.fireEvent("preparedata", this, data, index, record);
18693         return data;
18694     },
18695
18696     onUpdate : function(ds, record){
18697         // Roo.log('on update');   
18698         this.clearSelections();
18699         var index = this.store.indexOf(record);
18700         var n = this.nodes[index];
18701         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18702         n.parentNode.removeChild(n);
18703         this.updateIndexes(index, index);
18704     },
18705
18706     
18707     
18708 // --------- FIXME     
18709     onAdd : function(ds, records, index)
18710     {
18711         //Roo.log(['on Add', ds, records, index] );        
18712         this.clearSelections();
18713         if(this.nodes.length == 0){
18714             this.refresh();
18715             return;
18716         }
18717         var n = this.nodes[index];
18718         for(var i = 0, len = records.length; i < len; i++){
18719             var d = this.prepareData(records[i].data, i, records[i]);
18720             if(n){
18721                 this.tpl.insertBefore(n, d);
18722             }else{
18723                 
18724                 this.tpl.append(this.el, d);
18725             }
18726         }
18727         this.updateIndexes(index);
18728     },
18729
18730     onRemove : function(ds, record, index){
18731        // Roo.log('onRemove');
18732         this.clearSelections();
18733         var el = this.dataName  ?
18734             this.el.child('.roo-tpl-' + this.dataName) :
18735             this.el; 
18736         
18737         el.dom.removeChild(this.nodes[index]);
18738         this.updateIndexes(index);
18739     },
18740
18741     /**
18742      * Refresh an individual node.
18743      * @param {Number} index
18744      */
18745     refreshNode : function(index){
18746         this.onUpdate(this.store, this.store.getAt(index));
18747     },
18748
18749     updateIndexes : function(startIndex, endIndex){
18750         var ns = this.nodes;
18751         startIndex = startIndex || 0;
18752         endIndex = endIndex || ns.length - 1;
18753         for(var i = startIndex; i <= endIndex; i++){
18754             ns[i].nodeIndex = i;
18755         }
18756     },
18757
18758     /**
18759      * Changes the data store this view uses and refresh the view.
18760      * @param {Store} store
18761      */
18762     setStore : function(store, initial){
18763         if(!initial && this.store){
18764             this.store.un("datachanged", this.refresh);
18765             this.store.un("add", this.onAdd);
18766             this.store.un("remove", this.onRemove);
18767             this.store.un("update", this.onUpdate);
18768             this.store.un("clear", this.refresh);
18769             this.store.un("beforeload", this.onBeforeLoad);
18770             this.store.un("load", this.onLoad);
18771             this.store.un("loadexception", this.onLoad);
18772         }
18773         if(store){
18774           
18775             store.on("datachanged", this.refresh, this);
18776             store.on("add", this.onAdd, this);
18777             store.on("remove", this.onRemove, this);
18778             store.on("update", this.onUpdate, this);
18779             store.on("clear", this.refresh, this);
18780             store.on("beforeload", this.onBeforeLoad, this);
18781             store.on("load", this.onLoad, this);
18782             store.on("loadexception", this.onLoad, this);
18783         }
18784         
18785         if(store){
18786             this.refresh();
18787         }
18788     },
18789     /**
18790      * onbeforeLoad - masks the loading area.
18791      *
18792      */
18793     onBeforeLoad : function(store,opts)
18794     {
18795          //Roo.log('onBeforeLoad');   
18796         if (!opts.add) {
18797             this.el.update("");
18798         }
18799         this.el.mask(this.mask ? this.mask : "Loading" ); 
18800     },
18801     onLoad : function ()
18802     {
18803         this.el.unmask();
18804     },
18805     
18806
18807     /**
18808      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18809      * @param {HTMLElement} node
18810      * @return {HTMLElement} The template node
18811      */
18812     findItemFromChild : function(node){
18813         var el = this.dataName  ?
18814             this.el.child('.roo-tpl-' + this.dataName,true) :
18815             this.el.dom; 
18816         
18817         if(!node || node.parentNode == el){
18818                     return node;
18819             }
18820             var p = node.parentNode;
18821             while(p && p != el){
18822             if(p.parentNode == el){
18823                 return p;
18824             }
18825             p = p.parentNode;
18826         }
18827             return null;
18828     },
18829
18830     /** @ignore */
18831     onClick : function(e){
18832         var item = this.findItemFromChild(e.getTarget());
18833         if(item){
18834             var index = this.indexOf(item);
18835             if(this.onItemClick(item, index, e) !== false){
18836                 this.fireEvent("click", this, index, item, e);
18837             }
18838         }else{
18839             this.clearSelections();
18840         }
18841     },
18842
18843     /** @ignore */
18844     onContextMenu : function(e){
18845         var item = this.findItemFromChild(e.getTarget());
18846         if(item){
18847             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18848         }
18849     },
18850
18851     /** @ignore */
18852     onDblClick : function(e){
18853         var item = this.findItemFromChild(e.getTarget());
18854         if(item){
18855             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18856         }
18857     },
18858
18859     onItemClick : function(item, index, e)
18860     {
18861         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18862             return false;
18863         }
18864         if (this.toggleSelect) {
18865             var m = this.isSelected(item) ? 'unselect' : 'select';
18866             //Roo.log(m);
18867             var _t = this;
18868             _t[m](item, true, false);
18869             return true;
18870         }
18871         if(this.multiSelect || this.singleSelect){
18872             if(this.multiSelect && e.shiftKey && this.lastSelection){
18873                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18874             }else{
18875                 this.select(item, this.multiSelect && e.ctrlKey);
18876                 this.lastSelection = item;
18877             }
18878             
18879             if(!this.tickable){
18880                 e.preventDefault();
18881             }
18882             
18883         }
18884         return true;
18885     },
18886
18887     /**
18888      * Get the number of selected nodes.
18889      * @return {Number}
18890      */
18891     getSelectionCount : function(){
18892         return this.selections.length;
18893     },
18894
18895     /**
18896      * Get the currently selected nodes.
18897      * @return {Array} An array of HTMLElements
18898      */
18899     getSelectedNodes : function(){
18900         return this.selections;
18901     },
18902
18903     /**
18904      * Get the indexes of the selected nodes.
18905      * @return {Array}
18906      */
18907     getSelectedIndexes : function(){
18908         var indexes = [], s = this.selections;
18909         for(var i = 0, len = s.length; i < len; i++){
18910             indexes.push(s[i].nodeIndex);
18911         }
18912         return indexes;
18913     },
18914
18915     /**
18916      * Clear all selections
18917      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18918      */
18919     clearSelections : function(suppressEvent){
18920         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18921             this.cmp.elements = this.selections;
18922             this.cmp.removeClass(this.selectedClass);
18923             this.selections = [];
18924             if(!suppressEvent){
18925                 this.fireEvent("selectionchange", this, this.selections);
18926             }
18927         }
18928     },
18929
18930     /**
18931      * Returns true if the passed node is selected
18932      * @param {HTMLElement/Number} node The node or node index
18933      * @return {Boolean}
18934      */
18935     isSelected : function(node){
18936         var s = this.selections;
18937         if(s.length < 1){
18938             return false;
18939         }
18940         node = this.getNode(node);
18941         return s.indexOf(node) !== -1;
18942     },
18943
18944     /**
18945      * Selects nodes.
18946      * @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
18947      * @param {Boolean} keepExisting (optional) true to keep existing selections
18948      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18949      */
18950     select : function(nodeInfo, keepExisting, suppressEvent){
18951         if(nodeInfo instanceof Array){
18952             if(!keepExisting){
18953                 this.clearSelections(true);
18954             }
18955             for(var i = 0, len = nodeInfo.length; i < len; i++){
18956                 this.select(nodeInfo[i], true, true);
18957             }
18958             return;
18959         } 
18960         var node = this.getNode(nodeInfo);
18961         if(!node || this.isSelected(node)){
18962             return; // already selected.
18963         }
18964         if(!keepExisting){
18965             this.clearSelections(true);
18966         }
18967         
18968         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18969             Roo.fly(node).addClass(this.selectedClass);
18970             this.selections.push(node);
18971             if(!suppressEvent){
18972                 this.fireEvent("selectionchange", this, this.selections);
18973             }
18974         }
18975         
18976         
18977     },
18978       /**
18979      * Unselects nodes.
18980      * @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
18981      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18982      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18983      */
18984     unselect : function(nodeInfo, keepExisting, suppressEvent)
18985     {
18986         if(nodeInfo instanceof Array){
18987             Roo.each(this.selections, function(s) {
18988                 this.unselect(s, nodeInfo);
18989             }, this);
18990             return;
18991         }
18992         var node = this.getNode(nodeInfo);
18993         if(!node || !this.isSelected(node)){
18994             //Roo.log("not selected");
18995             return; // not selected.
18996         }
18997         // fireevent???
18998         var ns = [];
18999         Roo.each(this.selections, function(s) {
19000             if (s == node ) {
19001                 Roo.fly(node).removeClass(this.selectedClass);
19002
19003                 return;
19004             }
19005             ns.push(s);
19006         },this);
19007         
19008         this.selections= ns;
19009         this.fireEvent("selectionchange", this, this.selections);
19010     },
19011
19012     /**
19013      * Gets a template node.
19014      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19015      * @return {HTMLElement} The node or null if it wasn't found
19016      */
19017     getNode : function(nodeInfo){
19018         if(typeof nodeInfo == "string"){
19019             return document.getElementById(nodeInfo);
19020         }else if(typeof nodeInfo == "number"){
19021             return this.nodes[nodeInfo];
19022         }
19023         return nodeInfo;
19024     },
19025
19026     /**
19027      * Gets a range template nodes.
19028      * @param {Number} startIndex
19029      * @param {Number} endIndex
19030      * @return {Array} An array of nodes
19031      */
19032     getNodes : function(start, end){
19033         var ns = this.nodes;
19034         start = start || 0;
19035         end = typeof end == "undefined" ? ns.length - 1 : end;
19036         var nodes = [];
19037         if(start <= end){
19038             for(var i = start; i <= end; i++){
19039                 nodes.push(ns[i]);
19040             }
19041         } else{
19042             for(var i = start; i >= end; i--){
19043                 nodes.push(ns[i]);
19044             }
19045         }
19046         return nodes;
19047     },
19048
19049     /**
19050      * Finds the index of the passed node
19051      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19052      * @return {Number} The index of the node or -1
19053      */
19054     indexOf : function(node){
19055         node = this.getNode(node);
19056         if(typeof node.nodeIndex == "number"){
19057             return node.nodeIndex;
19058         }
19059         var ns = this.nodes;
19060         for(var i = 0, len = ns.length; i < len; i++){
19061             if(ns[i] == node){
19062                 return i;
19063             }
19064         }
19065         return -1;
19066     }
19067 });
19068 /*
19069  * - LGPL
19070  *
19071  * based on jquery fullcalendar
19072  * 
19073  */
19074
19075 Roo.bootstrap = Roo.bootstrap || {};
19076 /**
19077  * @class Roo.bootstrap.Calendar
19078  * @extends Roo.bootstrap.Component
19079  * Bootstrap Calendar class
19080  * @cfg {Boolean} loadMask (true|false) default false
19081  * @cfg {Object} header generate the user specific header of the calendar, default false
19082
19083  * @constructor
19084  * Create a new Container
19085  * @param {Object} config The config object
19086  */
19087
19088
19089
19090 Roo.bootstrap.Calendar = function(config){
19091     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19092      this.addEvents({
19093         /**
19094              * @event select
19095              * Fires when a date is selected
19096              * @param {DatePicker} this
19097              * @param {Date} date The selected date
19098              */
19099         'select': true,
19100         /**
19101              * @event monthchange
19102              * Fires when the displayed month changes 
19103              * @param {DatePicker} this
19104              * @param {Date} date The selected month
19105              */
19106         'monthchange': true,
19107         /**
19108              * @event evententer
19109              * Fires when mouse over an event
19110              * @param {Calendar} this
19111              * @param {event} Event
19112              */
19113         'evententer': true,
19114         /**
19115              * @event eventleave
19116              * Fires when the mouse leaves an
19117              * @param {Calendar} this
19118              * @param {event}
19119              */
19120         'eventleave': true,
19121         /**
19122              * @event eventclick
19123              * Fires when the mouse click an
19124              * @param {Calendar} this
19125              * @param {event}
19126              */
19127         'eventclick': true
19128         
19129     });
19130
19131 };
19132
19133 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19134     
19135      /**
19136      * @cfg {Number} startDay
19137      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19138      */
19139     startDay : 0,
19140     
19141     loadMask : false,
19142     
19143     header : false,
19144       
19145     getAutoCreate : function(){
19146         
19147         
19148         var fc_button = function(name, corner, style, content ) {
19149             return Roo.apply({},{
19150                 tag : 'span',
19151                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19152                          (corner.length ?
19153                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19154                             ''
19155                         ),
19156                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19157                 unselectable: 'on'
19158             });
19159         };
19160         
19161         var header = {};
19162         
19163         if(!this.header){
19164             header = {
19165                 tag : 'table',
19166                 cls : 'fc-header',
19167                 style : 'width:100%',
19168                 cn : [
19169                     {
19170                         tag: 'tr',
19171                         cn : [
19172                             {
19173                                 tag : 'td',
19174                                 cls : 'fc-header-left',
19175                                 cn : [
19176                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19177                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19178                                     { tag: 'span', cls: 'fc-header-space' },
19179                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19180
19181
19182                                 ]
19183                             },
19184
19185                             {
19186                                 tag : 'td',
19187                                 cls : 'fc-header-center',
19188                                 cn : [
19189                                     {
19190                                         tag: 'span',
19191                                         cls: 'fc-header-title',
19192                                         cn : {
19193                                             tag: 'H2',
19194                                             html : 'month / year'
19195                                         }
19196                                     }
19197
19198                                 ]
19199                             },
19200                             {
19201                                 tag : 'td',
19202                                 cls : 'fc-header-right',
19203                                 cn : [
19204                               /*      fc_button('month', 'left', '', 'month' ),
19205                                     fc_button('week', '', '', 'week' ),
19206                                     fc_button('day', 'right', '', 'day' )
19207                                 */    
19208
19209                                 ]
19210                             }
19211
19212                         ]
19213                     }
19214                 ]
19215             };
19216         }
19217         
19218         header = this.header;
19219         
19220        
19221         var cal_heads = function() {
19222             var ret = [];
19223             // fixme - handle this.
19224             
19225             for (var i =0; i < Date.dayNames.length; i++) {
19226                 var d = Date.dayNames[i];
19227                 ret.push({
19228                     tag: 'th',
19229                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19230                     html : d.substring(0,3)
19231                 });
19232                 
19233             }
19234             ret[0].cls += ' fc-first';
19235             ret[6].cls += ' fc-last';
19236             return ret;
19237         };
19238         var cal_cell = function(n) {
19239             return  {
19240                 tag: 'td',
19241                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19242                 cn : [
19243                     {
19244                         cn : [
19245                             {
19246                                 cls: 'fc-day-number',
19247                                 html: 'D'
19248                             },
19249                             {
19250                                 cls: 'fc-day-content',
19251                              
19252                                 cn : [
19253                                      {
19254                                         style: 'position: relative;' // height: 17px;
19255                                     }
19256                                 ]
19257                             }
19258                             
19259                             
19260                         ]
19261                     }
19262                 ]
19263                 
19264             }
19265         };
19266         var cal_rows = function() {
19267             
19268             var ret = [];
19269             for (var r = 0; r < 6; r++) {
19270                 var row= {
19271                     tag : 'tr',
19272                     cls : 'fc-week',
19273                     cn : []
19274                 };
19275                 
19276                 for (var i =0; i < Date.dayNames.length; i++) {
19277                     var d = Date.dayNames[i];
19278                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19279
19280                 }
19281                 row.cn[0].cls+=' fc-first';
19282                 row.cn[0].cn[0].style = 'min-height:90px';
19283                 row.cn[6].cls+=' fc-last';
19284                 ret.push(row);
19285                 
19286             }
19287             ret[0].cls += ' fc-first';
19288             ret[4].cls += ' fc-prev-last';
19289             ret[5].cls += ' fc-last';
19290             return ret;
19291             
19292         };
19293         
19294         var cal_table = {
19295             tag: 'table',
19296             cls: 'fc-border-separate',
19297             style : 'width:100%',
19298             cellspacing  : 0,
19299             cn : [
19300                 { 
19301                     tag: 'thead',
19302                     cn : [
19303                         { 
19304                             tag: 'tr',
19305                             cls : 'fc-first fc-last',
19306                             cn : cal_heads()
19307                         }
19308                     ]
19309                 },
19310                 { 
19311                     tag: 'tbody',
19312                     cn : cal_rows()
19313                 }
19314                   
19315             ]
19316         };
19317          
19318          var cfg = {
19319             cls : 'fc fc-ltr',
19320             cn : [
19321                 header,
19322                 {
19323                     cls : 'fc-content',
19324                     style : "position: relative;",
19325                     cn : [
19326                         {
19327                             cls : 'fc-view fc-view-month fc-grid',
19328                             style : 'position: relative',
19329                             unselectable : 'on',
19330                             cn : [
19331                                 {
19332                                     cls : 'fc-event-container',
19333                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19334                                 },
19335                                 cal_table
19336                             ]
19337                         }
19338                     ]
19339     
19340                 }
19341            ] 
19342             
19343         };
19344         
19345          
19346         
19347         return cfg;
19348     },
19349     
19350     
19351     initEvents : function()
19352     {
19353         if(!this.store){
19354             throw "can not find store for calendar";
19355         }
19356         
19357         var mark = {
19358             tag: "div",
19359             cls:"x-dlg-mask",
19360             style: "text-align:center",
19361             cn: [
19362                 {
19363                     tag: "div",
19364                     style: "background-color:white;width:50%;margin:250 auto",
19365                     cn: [
19366                         {
19367                             tag: "img",
19368                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19369                         },
19370                         {
19371                             tag: "span",
19372                             html: "Loading"
19373                         }
19374                         
19375                     ]
19376                 }
19377             ]
19378         };
19379         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19380         
19381         var size = this.el.select('.fc-content', true).first().getSize();
19382         this.maskEl.setSize(size.width, size.height);
19383         this.maskEl.enableDisplayMode("block");
19384         if(!this.loadMask){
19385             this.maskEl.hide();
19386         }
19387         
19388         this.store = Roo.factory(this.store, Roo.data);
19389         this.store.on('load', this.onLoad, this);
19390         this.store.on('beforeload', this.onBeforeLoad, this);
19391         
19392         this.resize();
19393         
19394         this.cells = this.el.select('.fc-day',true);
19395         //Roo.log(this.cells);
19396         this.textNodes = this.el.query('.fc-day-number');
19397         this.cells.addClassOnOver('fc-state-hover');
19398         
19399         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19400         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19401         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19402         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19403         
19404         this.on('monthchange', this.onMonthChange, this);
19405         
19406         this.update(new Date().clearTime());
19407     },
19408     
19409     resize : function() {
19410         var sz  = this.el.getSize();
19411         
19412         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19413         this.el.select('.fc-day-content div',true).setHeight(34);
19414     },
19415     
19416     
19417     // private
19418     showPrevMonth : function(e){
19419         this.update(this.activeDate.add("mo", -1));
19420     },
19421     showToday : function(e){
19422         this.update(new Date().clearTime());
19423     },
19424     // private
19425     showNextMonth : function(e){
19426         this.update(this.activeDate.add("mo", 1));
19427     },
19428
19429     // private
19430     showPrevYear : function(){
19431         this.update(this.activeDate.add("y", -1));
19432     },
19433
19434     // private
19435     showNextYear : function(){
19436         this.update(this.activeDate.add("y", 1));
19437     },
19438
19439     
19440    // private
19441     update : function(date)
19442     {
19443         var vd = this.activeDate;
19444         this.activeDate = date;
19445 //        if(vd && this.el){
19446 //            var t = date.getTime();
19447 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19448 //                Roo.log('using add remove');
19449 //                
19450 //                this.fireEvent('monthchange', this, date);
19451 //                
19452 //                this.cells.removeClass("fc-state-highlight");
19453 //                this.cells.each(function(c){
19454 //                   if(c.dateValue == t){
19455 //                       c.addClass("fc-state-highlight");
19456 //                       setTimeout(function(){
19457 //                            try{c.dom.firstChild.focus();}catch(e){}
19458 //                       }, 50);
19459 //                       return false;
19460 //                   }
19461 //                   return true;
19462 //                });
19463 //                return;
19464 //            }
19465 //        }
19466         
19467         var days = date.getDaysInMonth();
19468         
19469         var firstOfMonth = date.getFirstDateOfMonth();
19470         var startingPos = firstOfMonth.getDay()-this.startDay;
19471         
19472         if(startingPos < this.startDay){
19473             startingPos += 7;
19474         }
19475         
19476         var pm = date.add(Date.MONTH, -1);
19477         var prevStart = pm.getDaysInMonth()-startingPos;
19478 //        
19479         this.cells = this.el.select('.fc-day',true);
19480         this.textNodes = this.el.query('.fc-day-number');
19481         this.cells.addClassOnOver('fc-state-hover');
19482         
19483         var cells = this.cells.elements;
19484         var textEls = this.textNodes;
19485         
19486         Roo.each(cells, function(cell){
19487             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19488         });
19489         
19490         days += startingPos;
19491
19492         // convert everything to numbers so it's fast
19493         var day = 86400000;
19494         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19495         //Roo.log(d);
19496         //Roo.log(pm);
19497         //Roo.log(prevStart);
19498         
19499         var today = new Date().clearTime().getTime();
19500         var sel = date.clearTime().getTime();
19501         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19502         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19503         var ddMatch = this.disabledDatesRE;
19504         var ddText = this.disabledDatesText;
19505         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19506         var ddaysText = this.disabledDaysText;
19507         var format = this.format;
19508         
19509         var setCellClass = function(cal, cell){
19510             cell.row = 0;
19511             cell.events = [];
19512             cell.more = [];
19513             //Roo.log('set Cell Class');
19514             cell.title = "";
19515             var t = d.getTime();
19516             
19517             //Roo.log(d);
19518             
19519             cell.dateValue = t;
19520             if(t == today){
19521                 cell.className += " fc-today";
19522                 cell.className += " fc-state-highlight";
19523                 cell.title = cal.todayText;
19524             }
19525             if(t == sel){
19526                 // disable highlight in other month..
19527                 //cell.className += " fc-state-highlight";
19528                 
19529             }
19530             // disabling
19531             if(t < min) {
19532                 cell.className = " fc-state-disabled";
19533                 cell.title = cal.minText;
19534                 return;
19535             }
19536             if(t > max) {
19537                 cell.className = " fc-state-disabled";
19538                 cell.title = cal.maxText;
19539                 return;
19540             }
19541             if(ddays){
19542                 if(ddays.indexOf(d.getDay()) != -1){
19543                     cell.title = ddaysText;
19544                     cell.className = " fc-state-disabled";
19545                 }
19546             }
19547             if(ddMatch && format){
19548                 var fvalue = d.dateFormat(format);
19549                 if(ddMatch.test(fvalue)){
19550                     cell.title = ddText.replace("%0", fvalue);
19551                     cell.className = " fc-state-disabled";
19552                 }
19553             }
19554             
19555             if (!cell.initialClassName) {
19556                 cell.initialClassName = cell.dom.className;
19557             }
19558             
19559             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19560         };
19561
19562         var i = 0;
19563         
19564         for(; i < startingPos; i++) {
19565             textEls[i].innerHTML = (++prevStart);
19566             d.setDate(d.getDate()+1);
19567             
19568             cells[i].className = "fc-past fc-other-month";
19569             setCellClass(this, cells[i]);
19570         }
19571         
19572         var intDay = 0;
19573         
19574         for(; i < days; i++){
19575             intDay = i - startingPos + 1;
19576             textEls[i].innerHTML = (intDay);
19577             d.setDate(d.getDate()+1);
19578             
19579             cells[i].className = ''; // "x-date-active";
19580             setCellClass(this, cells[i]);
19581         }
19582         var extraDays = 0;
19583         
19584         for(; i < 42; i++) {
19585             textEls[i].innerHTML = (++extraDays);
19586             d.setDate(d.getDate()+1);
19587             
19588             cells[i].className = "fc-future fc-other-month";
19589             setCellClass(this, cells[i]);
19590         }
19591         
19592         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19593         
19594         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19595         
19596         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19597         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19598         
19599         if(totalRows != 6){
19600             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19601             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19602         }
19603         
19604         this.fireEvent('monthchange', this, date);
19605         
19606         
19607         /*
19608         if(!this.internalRender){
19609             var main = this.el.dom.firstChild;
19610             var w = main.offsetWidth;
19611             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19612             Roo.fly(main).setWidth(w);
19613             this.internalRender = true;
19614             // opera does not respect the auto grow header center column
19615             // then, after it gets a width opera refuses to recalculate
19616             // without a second pass
19617             if(Roo.isOpera && !this.secondPass){
19618                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19619                 this.secondPass = true;
19620                 this.update.defer(10, this, [date]);
19621             }
19622         }
19623         */
19624         
19625     },
19626     
19627     findCell : function(dt) {
19628         dt = dt.clearTime().getTime();
19629         var ret = false;
19630         this.cells.each(function(c){
19631             //Roo.log("check " +c.dateValue + '?=' + dt);
19632             if(c.dateValue == dt){
19633                 ret = c;
19634                 return false;
19635             }
19636             return true;
19637         });
19638         
19639         return ret;
19640     },
19641     
19642     findCells : function(ev) {
19643         var s = ev.start.clone().clearTime().getTime();
19644        // Roo.log(s);
19645         var e= ev.end.clone().clearTime().getTime();
19646        // Roo.log(e);
19647         var ret = [];
19648         this.cells.each(function(c){
19649              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19650             
19651             if(c.dateValue > e){
19652                 return ;
19653             }
19654             if(c.dateValue < s){
19655                 return ;
19656             }
19657             ret.push(c);
19658         });
19659         
19660         return ret;    
19661     },
19662     
19663 //    findBestRow: function(cells)
19664 //    {
19665 //        var ret = 0;
19666 //        
19667 //        for (var i =0 ; i < cells.length;i++) {
19668 //            ret  = Math.max(cells[i].rows || 0,ret);
19669 //        }
19670 //        return ret;
19671 //        
19672 //    },
19673     
19674     
19675     addItem : function(ev)
19676     {
19677         // look for vertical location slot in
19678         var cells = this.findCells(ev);
19679         
19680 //        ev.row = this.findBestRow(cells);
19681         
19682         // work out the location.
19683         
19684         var crow = false;
19685         var rows = [];
19686         for(var i =0; i < cells.length; i++) {
19687             
19688             cells[i].row = cells[0].row;
19689             
19690             if(i == 0){
19691                 cells[i].row = cells[i].row + 1;
19692             }
19693             
19694             if (!crow) {
19695                 crow = {
19696                     start : cells[i],
19697                     end :  cells[i]
19698                 };
19699                 continue;
19700             }
19701             if (crow.start.getY() == cells[i].getY()) {
19702                 // on same row.
19703                 crow.end = cells[i];
19704                 continue;
19705             }
19706             // different row.
19707             rows.push(crow);
19708             crow = {
19709                 start: cells[i],
19710                 end : cells[i]
19711             };
19712             
19713         }
19714         
19715         rows.push(crow);
19716         ev.els = [];
19717         ev.rows = rows;
19718         ev.cells = cells;
19719         
19720         cells[0].events.push(ev);
19721         
19722         this.calevents.push(ev);
19723     },
19724     
19725     clearEvents: function() {
19726         
19727         if(!this.calevents){
19728             return;
19729         }
19730         
19731         Roo.each(this.cells.elements, function(c){
19732             c.row = 0;
19733             c.events = [];
19734             c.more = [];
19735         });
19736         
19737         Roo.each(this.calevents, function(e) {
19738             Roo.each(e.els, function(el) {
19739                 el.un('mouseenter' ,this.onEventEnter, this);
19740                 el.un('mouseleave' ,this.onEventLeave, this);
19741                 el.remove();
19742             },this);
19743         },this);
19744         
19745         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19746             e.remove();
19747         });
19748         
19749     },
19750     
19751     renderEvents: function()
19752     {   
19753         var _this = this;
19754         
19755         this.cells.each(function(c) {
19756             
19757             if(c.row < 5){
19758                 return;
19759             }
19760             
19761             var ev = c.events;
19762             
19763             var r = 4;
19764             if(c.row != c.events.length){
19765                 r = 4 - (4 - (c.row - c.events.length));
19766             }
19767             
19768             c.events = ev.slice(0, r);
19769             c.more = ev.slice(r);
19770             
19771             if(c.more.length && c.more.length == 1){
19772                 c.events.push(c.more.pop());
19773             }
19774             
19775             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19776             
19777         });
19778             
19779         this.cells.each(function(c) {
19780             
19781             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19782             
19783             
19784             for (var e = 0; e < c.events.length; e++){
19785                 var ev = c.events[e];
19786                 var rows = ev.rows;
19787                 
19788                 for(var i = 0; i < rows.length; i++) {
19789                 
19790                     // how many rows should it span..
19791
19792                     var  cfg = {
19793                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19794                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19795
19796                         unselectable : "on",
19797                         cn : [
19798                             {
19799                                 cls: 'fc-event-inner',
19800                                 cn : [
19801     //                                {
19802     //                                  tag:'span',
19803     //                                  cls: 'fc-event-time',
19804     //                                  html : cells.length > 1 ? '' : ev.time
19805     //                                },
19806                                     {
19807                                       tag:'span',
19808                                       cls: 'fc-event-title',
19809                                       html : String.format('{0}', ev.title)
19810                                     }
19811
19812
19813                                 ]
19814                             },
19815                             {
19816                                 cls: 'ui-resizable-handle ui-resizable-e',
19817                                 html : '&nbsp;&nbsp;&nbsp'
19818                             }
19819
19820                         ]
19821                     };
19822
19823                     if (i == 0) {
19824                         cfg.cls += ' fc-event-start';
19825                     }
19826                     if ((i+1) == rows.length) {
19827                         cfg.cls += ' fc-event-end';
19828                     }
19829
19830                     var ctr = _this.el.select('.fc-event-container',true).first();
19831                     var cg = ctr.createChild(cfg);
19832
19833                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19834                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19835
19836                     var r = (c.more.length) ? 1 : 0;
19837                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19838                     cg.setWidth(ebox.right - sbox.x -2);
19839
19840                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19841                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19842                     cg.on('click', _this.onEventClick, _this, ev);
19843
19844                     ev.els.push(cg);
19845                     
19846                 }
19847                 
19848             }
19849             
19850             
19851             if(c.more.length){
19852                 var  cfg = {
19853                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19854                     style : 'position: absolute',
19855                     unselectable : "on",
19856                     cn : [
19857                         {
19858                             cls: 'fc-event-inner',
19859                             cn : [
19860                                 {
19861                                   tag:'span',
19862                                   cls: 'fc-event-title',
19863                                   html : 'More'
19864                                 }
19865
19866
19867                             ]
19868                         },
19869                         {
19870                             cls: 'ui-resizable-handle ui-resizable-e',
19871                             html : '&nbsp;&nbsp;&nbsp'
19872                         }
19873
19874                     ]
19875                 };
19876
19877                 var ctr = _this.el.select('.fc-event-container',true).first();
19878                 var cg = ctr.createChild(cfg);
19879
19880                 var sbox = c.select('.fc-day-content',true).first().getBox();
19881                 var ebox = c.select('.fc-day-content',true).first().getBox();
19882                 //Roo.log(cg);
19883                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19884                 cg.setWidth(ebox.right - sbox.x -2);
19885
19886                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19887                 
19888             }
19889             
19890         });
19891         
19892         
19893         
19894     },
19895     
19896     onEventEnter: function (e, el,event,d) {
19897         this.fireEvent('evententer', this, el, event);
19898     },
19899     
19900     onEventLeave: function (e, el,event,d) {
19901         this.fireEvent('eventleave', this, el, event);
19902     },
19903     
19904     onEventClick: function (e, el,event,d) {
19905         this.fireEvent('eventclick', this, el, event);
19906     },
19907     
19908     onMonthChange: function () {
19909         this.store.load();
19910     },
19911     
19912     onMoreEventClick: function(e, el, more)
19913     {
19914         var _this = this;
19915         
19916         this.calpopover.placement = 'right';
19917         this.calpopover.setTitle('More');
19918         
19919         this.calpopover.setContent('');
19920         
19921         var ctr = this.calpopover.el.select('.popover-content', true).first();
19922         
19923         Roo.each(more, function(m){
19924             var cfg = {
19925                 cls : 'fc-event-hori fc-event-draggable',
19926                 html : m.title
19927             };
19928             var cg = ctr.createChild(cfg);
19929             
19930             cg.on('click', _this.onEventClick, _this, m);
19931         });
19932         
19933         this.calpopover.show(el);
19934         
19935         
19936     },
19937     
19938     onLoad: function () 
19939     {   
19940         this.calevents = [];
19941         var cal = this;
19942         
19943         if(this.store.getCount() > 0){
19944             this.store.data.each(function(d){
19945                cal.addItem({
19946                     id : d.data.id,
19947                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19948                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19949                     time : d.data.start_time,
19950                     title : d.data.title,
19951                     description : d.data.description,
19952                     venue : d.data.venue
19953                 });
19954             });
19955         }
19956         
19957         this.renderEvents();
19958         
19959         if(this.calevents.length && this.loadMask){
19960             this.maskEl.hide();
19961         }
19962     },
19963     
19964     onBeforeLoad: function()
19965     {
19966         this.clearEvents();
19967         if(this.loadMask){
19968             this.maskEl.show();
19969         }
19970     }
19971 });
19972
19973  
19974  /*
19975  * - LGPL
19976  *
19977  * element
19978  * 
19979  */
19980
19981 /**
19982  * @class Roo.bootstrap.Popover
19983  * @extends Roo.bootstrap.Component
19984  * Bootstrap Popover class
19985  * @cfg {String} html contents of the popover   (or false to use children..)
19986  * @cfg {String} title of popover (or false to hide)
19987  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19988  * @cfg {String} trigger click || hover (or false to trigger manually)
19989  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19990  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19991  *      - if false and it has a 'parent' then it will be automatically added to that element
19992  *      - if string - Roo.get  will be called 
19993  * @cfg {Number} delay - delay before showing
19994  
19995  * @constructor
19996  * Create a new Popover
19997  * @param {Object} config The config object
19998  */
19999
20000 Roo.bootstrap.Popover = function(config){
20001     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20002     
20003     this.addEvents({
20004         // raw events
20005          /**
20006          * @event show
20007          * After the popover show
20008          * 
20009          * @param {Roo.bootstrap.Popover} this
20010          */
20011         "show" : true,
20012         /**
20013          * @event hide
20014          * After the popover hide
20015          * 
20016          * @param {Roo.bootstrap.Popover} this
20017          */
20018         "hide" : true
20019     });
20020 };
20021
20022 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20023     
20024     title: false,
20025     html: false,
20026     
20027     placement : 'right',
20028     trigger : 'hover', // hover
20029     modal : false,
20030     delay : 0,
20031     
20032     over: false,
20033     
20034     can_build_overlaid : false,
20035     
20036     maskEl : false, // the mask element
20037     headerEl : false,
20038     contentEl : false,
20039     alignEl : false, // when show is called with an element - this get's stored.
20040     
20041     getChildContainer : function()
20042     {
20043         return this.contentEl;
20044         
20045     },
20046     getPopoverHeader : function()
20047     {
20048         this.title = true; // flag not to hide it..
20049         this.headerEl.addClass('p-0');
20050         return this.headerEl
20051     },
20052     
20053     
20054     getAutoCreate : function(){
20055          
20056         var cfg = {
20057            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20058            style: 'display:block',
20059            cn : [
20060                 {
20061                     cls : 'arrow'
20062                 },
20063                 {
20064                     cls : 'popover-inner ',
20065                     cn : [
20066                         {
20067                             tag: 'h3',
20068                             cls: 'popover-title popover-header',
20069                             html : this.title === false ? '' : this.title
20070                         },
20071                         {
20072                             cls : 'popover-content popover-body '  + (this.cls || ''),
20073                             html : this.html || ''
20074                         }
20075                     ]
20076                     
20077                 }
20078            ]
20079         };
20080         
20081         return cfg;
20082     },
20083     /**
20084      * @param {string} the title
20085      */
20086     setTitle: function(str)
20087     {
20088         this.title = str;
20089         if (this.el) {
20090             this.headerEl.dom.innerHTML = str;
20091         }
20092         
20093     },
20094     /**
20095      * @param {string} the body content
20096      */
20097     setContent: function(str)
20098     {
20099         this.html = str;
20100         if (this.contentEl) {
20101             this.contentEl.dom.innerHTML = str;
20102         }
20103         
20104     },
20105     // as it get's added to the bottom of the page.
20106     onRender : function(ct, position)
20107     {
20108         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20109         
20110         
20111         
20112         if(!this.el){
20113             var cfg = Roo.apply({},  this.getAutoCreate());
20114             cfg.id = Roo.id();
20115             
20116             if (this.cls) {
20117                 cfg.cls += ' ' + this.cls;
20118             }
20119             if (this.style) {
20120                 cfg.style = this.style;
20121             }
20122             //Roo.log("adding to ");
20123             this.el = Roo.get(document.body).createChild(cfg, position);
20124 //            Roo.log(this.el);
20125         }
20126         
20127         this.contentEl = this.el.select('.popover-content',true).first();
20128         this.headerEl =  this.el.select('.popover-title',true).first();
20129         
20130         var nitems = [];
20131         if(typeof(this.items) != 'undefined'){
20132             var items = this.items;
20133             delete this.items;
20134
20135             for(var i =0;i < items.length;i++) {
20136                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20137             }
20138         }
20139
20140         this.items = nitems;
20141         
20142         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20143         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20144         
20145         
20146         
20147         this.initEvents();
20148     },
20149     
20150     resizeMask : function()
20151     {
20152         this.maskEl.setSize(
20153             Roo.lib.Dom.getViewWidth(true),
20154             Roo.lib.Dom.getViewHeight(true)
20155         );
20156     },
20157     
20158     initEvents : function()
20159     {
20160         
20161         if (!this.modal) { 
20162             Roo.bootstrap.Popover.register(this);
20163         }
20164          
20165         this.arrowEl = this.el.select('.arrow',true).first();
20166         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20167         this.el.enableDisplayMode('block');
20168         this.el.hide();
20169  
20170         
20171         if (this.over === false && !this.parent()) {
20172             return; 
20173         }
20174         if (this.triggers === false) {
20175             return;
20176         }
20177          
20178         // support parent
20179         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20180         var triggers = this.trigger ? this.trigger.split(' ') : [];
20181         Roo.each(triggers, function(trigger) {
20182         
20183             if (trigger == 'click') {
20184                 on_el.on('click', this.toggle, this);
20185             } else if (trigger != 'manual') {
20186                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20187                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20188       
20189                 on_el.on(eventIn  ,this.enter, this);
20190                 on_el.on(eventOut, this.leave, this);
20191             }
20192         }, this);
20193     },
20194     
20195     
20196     // private
20197     timeout : null,
20198     hoverState : null,
20199     
20200     toggle : function () {
20201         this.hoverState == 'in' ? this.leave() : this.enter();
20202     },
20203     
20204     enter : function () {
20205         
20206         clearTimeout(this.timeout);
20207     
20208         this.hoverState = 'in';
20209     
20210         if (!this.delay || !this.delay.show) {
20211             this.show();
20212             return;
20213         }
20214         var _t = this;
20215         this.timeout = setTimeout(function () {
20216             if (_t.hoverState == 'in') {
20217                 _t.show();
20218             }
20219         }, this.delay.show)
20220     },
20221     
20222     leave : function() {
20223         clearTimeout(this.timeout);
20224     
20225         this.hoverState = 'out';
20226     
20227         if (!this.delay || !this.delay.hide) {
20228             this.hide();
20229             return;
20230         }
20231         var _t = this;
20232         this.timeout = setTimeout(function () {
20233             if (_t.hoverState == 'out') {
20234                 _t.hide();
20235             }
20236         }, this.delay.hide)
20237     },
20238     /**
20239      * Show the popover
20240      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20241      * @param {string} (left|right|top|bottom) position
20242      */
20243     show : function (on_el, placement)
20244     {
20245         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20246         on_el = on_el || false; // default to false
20247          
20248         if (!on_el) {
20249             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20250                 on_el = this.parent().el;
20251             } else if (this.over) {
20252                 on_el = Roo.get(this.over);
20253             }
20254             
20255         }
20256         
20257         this.alignEl = Roo.get( on_el );
20258
20259         if (!this.el) {
20260             this.render(document.body);
20261         }
20262         
20263         
20264          
20265         
20266         if (this.title === false) {
20267             this.headerEl.hide();
20268         }
20269         
20270        
20271         this.el.show();
20272         this.el.dom.style.display = 'block';
20273          
20274  
20275         if (this.alignEl) {
20276             this.updatePosition(this.placement, true);
20277              
20278         } else {
20279             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20280             var es = this.el.getSize();
20281             var x = Roo.lib.Dom.getViewWidth()/2;
20282             var y = Roo.lib.Dom.getViewHeight()/2;
20283             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20284             
20285         }
20286
20287         
20288         //var arrow = this.el.select('.arrow',true).first();
20289         //arrow.set(align[2], 
20290         
20291         this.el.addClass('in');
20292         
20293          
20294         
20295         this.hoverState = 'in';
20296         
20297         if (this.modal) {
20298             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20299             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20300             this.maskEl.dom.style.display = 'block';
20301             this.maskEl.addClass('show');
20302         }
20303         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20304  
20305         this.fireEvent('show', this);
20306         
20307     },
20308     /**
20309      * fire this manually after loading a grid in the table for example
20310      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20311      * @param {Boolean} try and move it if we cant get right position.
20312      */
20313     updatePosition : function(placement, try_move)
20314     {
20315         // allow for calling with no parameters
20316         placement = placement   ? placement :  this.placement;
20317         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20318         
20319         this.el.removeClass([
20320             'fade','top','bottom', 'left', 'right','in',
20321             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20322         ]);
20323         this.el.addClass(placement + ' bs-popover-' + placement);
20324         
20325         if (!this.alignEl ) {
20326             return false;
20327         }
20328         
20329         switch (placement) {
20330             case 'right':
20331                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20332                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20333                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20334                     //normal display... or moved up/down.
20335                     this.el.setXY(offset);
20336                     var xy = this.alignEl.getAnchorXY('tr', false);
20337                     xy[0]+=2;xy[1]+=5;
20338                     this.arrowEl.setXY(xy);
20339                     return true;
20340                 }
20341                 // continue through...
20342                 return this.updatePosition('left', false);
20343                 
20344             
20345             case 'left':
20346                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20347                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20348                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20349                     //normal display... or moved up/down.
20350                     this.el.setXY(offset);
20351                     var xy = this.alignEl.getAnchorXY('tl', false);
20352                     xy[0]-=10;xy[1]+=5; // << fix me
20353                     this.arrowEl.setXY(xy);
20354                     return true;
20355                 }
20356                 // call self...
20357                 return this.updatePosition('right', false);
20358             
20359             case 'top':
20360                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20361                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20362                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20363                     //normal display... or moved up/down.
20364                     this.el.setXY(offset);
20365                     var xy = this.alignEl.getAnchorXY('t', false);
20366                     xy[1]-=10; // << fix me
20367                     this.arrowEl.setXY(xy);
20368                     return true;
20369                 }
20370                 // fall through
20371                return this.updatePosition('bottom', false);
20372             
20373             case 'bottom':
20374                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20375                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20376                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20377                     //normal display... or moved up/down.
20378                     this.el.setXY(offset);
20379                     var xy = this.alignEl.getAnchorXY('b', false);
20380                      xy[1]+=2; // << fix me
20381                     this.arrowEl.setXY(xy);
20382                     return true;
20383                 }
20384                 // fall through
20385                 return this.updatePosition('top', false);
20386                 
20387             
20388         }
20389         
20390         
20391         return false;
20392     },
20393     
20394     hide : function()
20395     {
20396         this.el.setXY([0,0]);
20397         this.el.removeClass('in');
20398         this.el.hide();
20399         this.hoverState = null;
20400         this.maskEl.hide(); // always..
20401         this.fireEvent('hide', this);
20402     }
20403     
20404 });
20405
20406
20407 Roo.apply(Roo.bootstrap.Popover, {
20408
20409     alignment : {
20410         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20411         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20412         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20413         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20414     },
20415     
20416     zIndex : 20001,
20417
20418     clickHander : false,
20419     
20420     
20421
20422     onMouseDown : function(e)
20423     {
20424         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20425             /// what is nothing is showing..
20426             this.hideAll();
20427         }
20428          
20429     },
20430     
20431     
20432     popups : [],
20433     
20434     register : function(popup)
20435     {
20436         if (!Roo.bootstrap.Popover.clickHandler) {
20437             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20438         }
20439         // hide other popups.
20440         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20441         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20442         this.hideAll(); //<< why?
20443         //this.popups.push(popup);
20444     },
20445     hideAll : function()
20446     {
20447         this.popups.forEach(function(p) {
20448             p.hide();
20449         });
20450     },
20451     onShow : function() {
20452         Roo.bootstrap.Popover.popups.push(this);
20453     },
20454     onHide : function() {
20455         Roo.bootstrap.Popover.popups.remove(this);
20456     } 
20457
20458 });/*
20459  * - LGPL
20460  *
20461  * Card header - holder for the card header elements.
20462  * 
20463  */
20464
20465 /**
20466  * @class Roo.bootstrap.PopoverNav
20467  * @extends Roo.bootstrap.NavGroup
20468  * Bootstrap Popover header navigation class
20469  * @constructor
20470  * Create a new Popover Header Navigation 
20471  * @param {Object} config The config object
20472  */
20473
20474 Roo.bootstrap.PopoverNav = function(config){
20475     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20476 };
20477
20478 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20479     
20480     
20481     container_method : 'getPopoverHeader' 
20482     
20483      
20484     
20485     
20486    
20487 });
20488
20489  
20490
20491  /*
20492  * - LGPL
20493  *
20494  * Progress
20495  * 
20496  */
20497
20498 /**
20499  * @class Roo.bootstrap.Progress
20500  * @extends Roo.bootstrap.Component
20501  * Bootstrap Progress class
20502  * @cfg {Boolean} striped striped of the progress bar
20503  * @cfg {Boolean} active animated of the progress bar
20504  * 
20505  * 
20506  * @constructor
20507  * Create a new Progress
20508  * @param {Object} config The config object
20509  */
20510
20511 Roo.bootstrap.Progress = function(config){
20512     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20513 };
20514
20515 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20516     
20517     striped : false,
20518     active: false,
20519     
20520     getAutoCreate : function(){
20521         var cfg = {
20522             tag: 'div',
20523             cls: 'progress'
20524         };
20525         
20526         
20527         if(this.striped){
20528             cfg.cls += ' progress-striped';
20529         }
20530       
20531         if(this.active){
20532             cfg.cls += ' active';
20533         }
20534         
20535         
20536         return cfg;
20537     }
20538    
20539 });
20540
20541  
20542
20543  /*
20544  * - LGPL
20545  *
20546  * ProgressBar
20547  * 
20548  */
20549
20550 /**
20551  * @class Roo.bootstrap.ProgressBar
20552  * @extends Roo.bootstrap.Component
20553  * Bootstrap ProgressBar class
20554  * @cfg {Number} aria_valuenow aria-value now
20555  * @cfg {Number} aria_valuemin aria-value min
20556  * @cfg {Number} aria_valuemax aria-value max
20557  * @cfg {String} label label for the progress bar
20558  * @cfg {String} panel (success | info | warning | danger )
20559  * @cfg {String} role role of the progress bar
20560  * @cfg {String} sr_only text
20561  * 
20562  * 
20563  * @constructor
20564  * Create a new ProgressBar
20565  * @param {Object} config The config object
20566  */
20567
20568 Roo.bootstrap.ProgressBar = function(config){
20569     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20570 };
20571
20572 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20573     
20574     aria_valuenow : 0,
20575     aria_valuemin : 0,
20576     aria_valuemax : 100,
20577     label : false,
20578     panel : false,
20579     role : false,
20580     sr_only: false,
20581     
20582     getAutoCreate : function()
20583     {
20584         
20585         var cfg = {
20586             tag: 'div',
20587             cls: 'progress-bar',
20588             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20589         };
20590         
20591         if(this.sr_only){
20592             cfg.cn = {
20593                 tag: 'span',
20594                 cls: 'sr-only',
20595                 html: this.sr_only
20596             }
20597         }
20598         
20599         if(this.role){
20600             cfg.role = this.role;
20601         }
20602         
20603         if(this.aria_valuenow){
20604             cfg['aria-valuenow'] = this.aria_valuenow;
20605         }
20606         
20607         if(this.aria_valuemin){
20608             cfg['aria-valuemin'] = this.aria_valuemin;
20609         }
20610         
20611         if(this.aria_valuemax){
20612             cfg['aria-valuemax'] = this.aria_valuemax;
20613         }
20614         
20615         if(this.label && !this.sr_only){
20616             cfg.html = this.label;
20617         }
20618         
20619         if(this.panel){
20620             cfg.cls += ' progress-bar-' + this.panel;
20621         }
20622         
20623         return cfg;
20624     },
20625     
20626     update : function(aria_valuenow)
20627     {
20628         this.aria_valuenow = aria_valuenow;
20629         
20630         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20631     }
20632    
20633 });
20634
20635  
20636
20637  /*
20638  * - LGPL
20639  *
20640  * column
20641  * 
20642  */
20643
20644 /**
20645  * @class Roo.bootstrap.TabGroup
20646  * @extends Roo.bootstrap.Column
20647  * Bootstrap Column class
20648  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20649  * @cfg {Boolean} carousel true to make the group behave like a carousel
20650  * @cfg {Boolean} bullets show bullets for the panels
20651  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20652  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20653  * @cfg {Boolean} showarrow (true|false) show arrow default true
20654  * 
20655  * @constructor
20656  * Create a new TabGroup
20657  * @param {Object} config The config object
20658  */
20659
20660 Roo.bootstrap.TabGroup = function(config){
20661     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20662     if (!this.navId) {
20663         this.navId = Roo.id();
20664     }
20665     this.tabs = [];
20666     Roo.bootstrap.TabGroup.register(this);
20667     
20668 };
20669
20670 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20671     
20672     carousel : false,
20673     transition : false,
20674     bullets : 0,
20675     timer : 0,
20676     autoslide : false,
20677     slideFn : false,
20678     slideOnTouch : false,
20679     showarrow : true,
20680     
20681     getAutoCreate : function()
20682     {
20683         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20684         
20685         cfg.cls += ' tab-content';
20686         
20687         if (this.carousel) {
20688             cfg.cls += ' carousel slide';
20689             
20690             cfg.cn = [{
20691                cls : 'carousel-inner',
20692                cn : []
20693             }];
20694         
20695             if(this.bullets  && !Roo.isTouch){
20696                 
20697                 var bullets = {
20698                     cls : 'carousel-bullets',
20699                     cn : []
20700                 };
20701                
20702                 if(this.bullets_cls){
20703                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20704                 }
20705                 
20706                 bullets.cn.push({
20707                     cls : 'clear'
20708                 });
20709                 
20710                 cfg.cn[0].cn.push(bullets);
20711             }
20712             
20713             if(this.showarrow){
20714                 cfg.cn[0].cn.push({
20715                     tag : 'div',
20716                     class : 'carousel-arrow',
20717                     cn : [
20718                         {
20719                             tag : 'div',
20720                             class : 'carousel-prev',
20721                             cn : [
20722                                 {
20723                                     tag : 'i',
20724                                     class : 'fa fa-chevron-left'
20725                                 }
20726                             ]
20727                         },
20728                         {
20729                             tag : 'div',
20730                             class : 'carousel-next',
20731                             cn : [
20732                                 {
20733                                     tag : 'i',
20734                                     class : 'fa fa-chevron-right'
20735                                 }
20736                             ]
20737                         }
20738                     ]
20739                 });
20740             }
20741             
20742         }
20743         
20744         return cfg;
20745     },
20746     
20747     initEvents:  function()
20748     {
20749 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20750 //            this.el.on("touchstart", this.onTouchStart, this);
20751 //        }
20752         
20753         if(this.autoslide){
20754             var _this = this;
20755             
20756             this.slideFn = window.setInterval(function() {
20757                 _this.showPanelNext();
20758             }, this.timer);
20759         }
20760         
20761         if(this.showarrow){
20762             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20763             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20764         }
20765         
20766         
20767     },
20768     
20769 //    onTouchStart : function(e, el, o)
20770 //    {
20771 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20772 //            return;
20773 //        }
20774 //        
20775 //        this.showPanelNext();
20776 //    },
20777     
20778     
20779     getChildContainer : function()
20780     {
20781         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20782     },
20783     
20784     /**
20785     * register a Navigation item
20786     * @param {Roo.bootstrap.NavItem} the navitem to add
20787     */
20788     register : function(item)
20789     {
20790         this.tabs.push( item);
20791         item.navId = this.navId; // not really needed..
20792         this.addBullet();
20793     
20794     },
20795     
20796     getActivePanel : function()
20797     {
20798         var r = false;
20799         Roo.each(this.tabs, function(t) {
20800             if (t.active) {
20801                 r = t;
20802                 return false;
20803             }
20804             return null;
20805         });
20806         return r;
20807         
20808     },
20809     getPanelByName : function(n)
20810     {
20811         var r = false;
20812         Roo.each(this.tabs, function(t) {
20813             if (t.tabId == n) {
20814                 r = t;
20815                 return false;
20816             }
20817             return null;
20818         });
20819         return r;
20820     },
20821     indexOfPanel : function(p)
20822     {
20823         var r = false;
20824         Roo.each(this.tabs, function(t,i) {
20825             if (t.tabId == p.tabId) {
20826                 r = i;
20827                 return false;
20828             }
20829             return null;
20830         });
20831         return r;
20832     },
20833     /**
20834      * show a specific panel
20835      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20836      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20837      */
20838     showPanel : function (pan)
20839     {
20840         if(this.transition || typeof(pan) == 'undefined'){
20841             Roo.log("waiting for the transitionend");
20842             return false;
20843         }
20844         
20845         if (typeof(pan) == 'number') {
20846             pan = this.tabs[pan];
20847         }
20848         
20849         if (typeof(pan) == 'string') {
20850             pan = this.getPanelByName(pan);
20851         }
20852         
20853         var cur = this.getActivePanel();
20854         
20855         if(!pan || !cur){
20856             Roo.log('pan or acitve pan is undefined');
20857             return false;
20858         }
20859         
20860         if (pan.tabId == this.getActivePanel().tabId) {
20861             return true;
20862         }
20863         
20864         if (false === cur.fireEvent('beforedeactivate')) {
20865             return false;
20866         }
20867         
20868         if(this.bullets > 0 && !Roo.isTouch){
20869             this.setActiveBullet(this.indexOfPanel(pan));
20870         }
20871         
20872         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20873             
20874             //class="carousel-item carousel-item-next carousel-item-left"
20875             
20876             this.transition = true;
20877             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20878             var lr = dir == 'next' ? 'left' : 'right';
20879             pan.el.addClass(dir); // or prev
20880             pan.el.addClass('carousel-item-' + dir); // or prev
20881             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20882             cur.el.addClass(lr); // or right
20883             pan.el.addClass(lr);
20884             cur.el.addClass('carousel-item-' +lr); // or right
20885             pan.el.addClass('carousel-item-' +lr);
20886             
20887             
20888             var _this = this;
20889             cur.el.on('transitionend', function() {
20890                 Roo.log("trans end?");
20891                 
20892                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20893                 pan.setActive(true);
20894                 
20895                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20896                 cur.setActive(false);
20897                 
20898                 _this.transition = false;
20899                 
20900             }, this, { single:  true } );
20901             
20902             return true;
20903         }
20904         
20905         cur.setActive(false);
20906         pan.setActive(true);
20907         
20908         return true;
20909         
20910     },
20911     showPanelNext : function()
20912     {
20913         var i = this.indexOfPanel(this.getActivePanel());
20914         
20915         if (i >= this.tabs.length - 1 && !this.autoslide) {
20916             return;
20917         }
20918         
20919         if (i >= this.tabs.length - 1 && this.autoslide) {
20920             i = -1;
20921         }
20922         
20923         this.showPanel(this.tabs[i+1]);
20924     },
20925     
20926     showPanelPrev : function()
20927     {
20928         var i = this.indexOfPanel(this.getActivePanel());
20929         
20930         if (i  < 1 && !this.autoslide) {
20931             return;
20932         }
20933         
20934         if (i < 1 && this.autoslide) {
20935             i = this.tabs.length;
20936         }
20937         
20938         this.showPanel(this.tabs[i-1]);
20939     },
20940     
20941     
20942     addBullet: function()
20943     {
20944         if(!this.bullets || Roo.isTouch){
20945             return;
20946         }
20947         var ctr = this.el.select('.carousel-bullets',true).first();
20948         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20949         var bullet = ctr.createChild({
20950             cls : 'bullet bullet-' + i
20951         },ctr.dom.lastChild);
20952         
20953         
20954         var _this = this;
20955         
20956         bullet.on('click', (function(e, el, o, ii, t){
20957
20958             e.preventDefault();
20959
20960             this.showPanel(ii);
20961
20962             if(this.autoslide && this.slideFn){
20963                 clearInterval(this.slideFn);
20964                 this.slideFn = window.setInterval(function() {
20965                     _this.showPanelNext();
20966                 }, this.timer);
20967             }
20968
20969         }).createDelegate(this, [i, bullet], true));
20970                 
20971         
20972     },
20973      
20974     setActiveBullet : function(i)
20975     {
20976         if(Roo.isTouch){
20977             return;
20978         }
20979         
20980         Roo.each(this.el.select('.bullet', true).elements, function(el){
20981             el.removeClass('selected');
20982         });
20983
20984         var bullet = this.el.select('.bullet-' + i, true).first();
20985         
20986         if(!bullet){
20987             return;
20988         }
20989         
20990         bullet.addClass('selected');
20991     }
20992     
20993     
20994   
20995 });
20996
20997  
20998
20999  
21000  
21001 Roo.apply(Roo.bootstrap.TabGroup, {
21002     
21003     groups: {},
21004      /**
21005     * register a Navigation Group
21006     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21007     */
21008     register : function(navgrp)
21009     {
21010         this.groups[navgrp.navId] = navgrp;
21011         
21012     },
21013     /**
21014     * fetch a Navigation Group based on the navigation ID
21015     * if one does not exist , it will get created.
21016     * @param {string} the navgroup to add
21017     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21018     */
21019     get: function(navId) {
21020         if (typeof(this.groups[navId]) == 'undefined') {
21021             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21022         }
21023         return this.groups[navId] ;
21024     }
21025     
21026     
21027     
21028 });
21029
21030  /*
21031  * - LGPL
21032  *
21033  * TabPanel
21034  * 
21035  */
21036
21037 /**
21038  * @class Roo.bootstrap.TabPanel
21039  * @extends Roo.bootstrap.Component
21040  * Bootstrap TabPanel class
21041  * @cfg {Boolean} active panel active
21042  * @cfg {String} html panel content
21043  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21044  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21045  * @cfg {String} href click to link..
21046  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21047  * 
21048  * 
21049  * @constructor
21050  * Create a new TabPanel
21051  * @param {Object} config The config object
21052  */
21053
21054 Roo.bootstrap.TabPanel = function(config){
21055     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21056     this.addEvents({
21057         /**
21058              * @event changed
21059              * Fires when the active status changes
21060              * @param {Roo.bootstrap.TabPanel} this
21061              * @param {Boolean} state the new state
21062             
21063          */
21064         'changed': true,
21065         /**
21066              * @event beforedeactivate
21067              * Fires before a tab is de-activated - can be used to do validation on a form.
21068              * @param {Roo.bootstrap.TabPanel} this
21069              * @return {Boolean} false if there is an error
21070             
21071          */
21072         'beforedeactivate': true
21073      });
21074     
21075     this.tabId = this.tabId || Roo.id();
21076   
21077 };
21078
21079 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21080     
21081     active: false,
21082     html: false,
21083     tabId: false,
21084     navId : false,
21085     href : '',
21086     touchSlide : false,
21087     getAutoCreate : function(){
21088         
21089         
21090         var cfg = {
21091             tag: 'div',
21092             // item is needed for carousel - not sure if it has any effect otherwise
21093             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21094             html: this.html || ''
21095         };
21096         
21097         if(this.active){
21098             cfg.cls += ' active';
21099         }
21100         
21101         if(this.tabId){
21102             cfg.tabId = this.tabId;
21103         }
21104         
21105         
21106         
21107         return cfg;
21108     },
21109     
21110     initEvents:  function()
21111     {
21112         var p = this.parent();
21113         
21114         this.navId = this.navId || p.navId;
21115         
21116         if (typeof(this.navId) != 'undefined') {
21117             // not really needed.. but just in case.. parent should be a NavGroup.
21118             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21119             
21120             tg.register(this);
21121             
21122             var i = tg.tabs.length - 1;
21123             
21124             if(this.active && tg.bullets > 0 && i < tg.bullets){
21125                 tg.setActiveBullet(i);
21126             }
21127         }
21128         
21129         this.el.on('click', this.onClick, this);
21130         
21131         if(Roo.isTouch && this.touchSlide){
21132             this.el.on("touchstart", this.onTouchStart, this);
21133             this.el.on("touchmove", this.onTouchMove, this);
21134             this.el.on("touchend", this.onTouchEnd, this);
21135         }
21136         
21137     },
21138     
21139     onRender : function(ct, position)
21140     {
21141         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21142     },
21143     
21144     setActive : function(state)
21145     {
21146         Roo.log("panel - set active " + this.tabId + "=" + state);
21147         
21148         this.active = state;
21149         if (!state) {
21150             this.el.removeClass('active');
21151             
21152         } else  if (!this.el.hasClass('active')) {
21153             this.el.addClass('active');
21154         }
21155         
21156         this.fireEvent('changed', this, state);
21157     },
21158     
21159     onClick : function(e)
21160     {
21161         e.preventDefault();
21162         
21163         if(!this.href.length){
21164             return;
21165         }
21166         
21167         window.location.href = this.href;
21168     },
21169     
21170     startX : 0,
21171     startY : 0,
21172     endX : 0,
21173     endY : 0,
21174     swiping : false,
21175     
21176     onTouchStart : function(e)
21177     {
21178         this.swiping = false;
21179         
21180         this.startX = e.browserEvent.touches[0].clientX;
21181         this.startY = e.browserEvent.touches[0].clientY;
21182     },
21183     
21184     onTouchMove : function(e)
21185     {
21186         this.swiping = true;
21187         
21188         this.endX = e.browserEvent.touches[0].clientX;
21189         this.endY = e.browserEvent.touches[0].clientY;
21190     },
21191     
21192     onTouchEnd : function(e)
21193     {
21194         if(!this.swiping){
21195             this.onClick(e);
21196             return;
21197         }
21198         
21199         var tabGroup = this.parent();
21200         
21201         if(this.endX > this.startX){ // swiping right
21202             tabGroup.showPanelPrev();
21203             return;
21204         }
21205         
21206         if(this.startX > this.endX){ // swiping left
21207             tabGroup.showPanelNext();
21208             return;
21209         }
21210     }
21211     
21212     
21213 });
21214  
21215
21216  
21217
21218  /*
21219  * - LGPL
21220  *
21221  * DateField
21222  * 
21223  */
21224
21225 /**
21226  * @class Roo.bootstrap.DateField
21227  * @extends Roo.bootstrap.Input
21228  * Bootstrap DateField class
21229  * @cfg {Number} weekStart default 0
21230  * @cfg {String} viewMode default empty, (months|years)
21231  * @cfg {String} minViewMode default empty, (months|years)
21232  * @cfg {Number} startDate default -Infinity
21233  * @cfg {Number} endDate default Infinity
21234  * @cfg {Boolean} todayHighlight default false
21235  * @cfg {Boolean} todayBtn default false
21236  * @cfg {Boolean} calendarWeeks default false
21237  * @cfg {Object} daysOfWeekDisabled default empty
21238  * @cfg {Boolean} singleMode default false (true | false)
21239  * 
21240  * @cfg {Boolean} keyboardNavigation default true
21241  * @cfg {String} language default en
21242  * 
21243  * @constructor
21244  * Create a new DateField
21245  * @param {Object} config The config object
21246  */
21247
21248 Roo.bootstrap.DateField = function(config){
21249     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21250      this.addEvents({
21251             /**
21252              * @event show
21253              * Fires when this field show.
21254              * @param {Roo.bootstrap.DateField} this
21255              * @param {Mixed} date The date value
21256              */
21257             show : true,
21258             /**
21259              * @event show
21260              * Fires when this field hide.
21261              * @param {Roo.bootstrap.DateField} this
21262              * @param {Mixed} date The date value
21263              */
21264             hide : true,
21265             /**
21266              * @event select
21267              * Fires when select a date.
21268              * @param {Roo.bootstrap.DateField} this
21269              * @param {Mixed} date The date value
21270              */
21271             select : true,
21272             /**
21273              * @event beforeselect
21274              * Fires when before select a date.
21275              * @param {Roo.bootstrap.DateField} this
21276              * @param {Mixed} date The date value
21277              */
21278             beforeselect : true
21279         });
21280 };
21281
21282 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21283     
21284     /**
21285      * @cfg {String} format
21286      * The default date format string which can be overriden for localization support.  The format must be
21287      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21288      */
21289     format : "m/d/y",
21290     /**
21291      * @cfg {String} altFormats
21292      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21293      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21294      */
21295     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21296     
21297     weekStart : 0,
21298     
21299     viewMode : '',
21300     
21301     minViewMode : '',
21302     
21303     todayHighlight : false,
21304     
21305     todayBtn: false,
21306     
21307     language: 'en',
21308     
21309     keyboardNavigation: true,
21310     
21311     calendarWeeks: false,
21312     
21313     startDate: -Infinity,
21314     
21315     endDate: Infinity,
21316     
21317     daysOfWeekDisabled: [],
21318     
21319     _events: [],
21320     
21321     singleMode : false,
21322     
21323     UTCDate: function()
21324     {
21325         return new Date(Date.UTC.apply(Date, arguments));
21326     },
21327     
21328     UTCToday: function()
21329     {
21330         var today = new Date();
21331         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21332     },
21333     
21334     getDate: function() {
21335             var d = this.getUTCDate();
21336             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21337     },
21338     
21339     getUTCDate: function() {
21340             return this.date;
21341     },
21342     
21343     setDate: function(d) {
21344             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21345     },
21346     
21347     setUTCDate: function(d) {
21348             this.date = d;
21349             this.setValue(this.formatDate(this.date));
21350     },
21351         
21352     onRender: function(ct, position)
21353     {
21354         
21355         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21356         
21357         this.language = this.language || 'en';
21358         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21359         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21360         
21361         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21362         this.format = this.format || 'm/d/y';
21363         this.isInline = false;
21364         this.isInput = true;
21365         this.component = this.el.select('.add-on', true).first() || false;
21366         this.component = (this.component && this.component.length === 0) ? false : this.component;
21367         this.hasInput = this.component && this.inputEl().length;
21368         
21369         if (typeof(this.minViewMode === 'string')) {
21370             switch (this.minViewMode) {
21371                 case 'months':
21372                     this.minViewMode = 1;
21373                     break;
21374                 case 'years':
21375                     this.minViewMode = 2;
21376                     break;
21377                 default:
21378                     this.minViewMode = 0;
21379                     break;
21380             }
21381         }
21382         
21383         if (typeof(this.viewMode === 'string')) {
21384             switch (this.viewMode) {
21385                 case 'months':
21386                     this.viewMode = 1;
21387                     break;
21388                 case 'years':
21389                     this.viewMode = 2;
21390                     break;
21391                 default:
21392                     this.viewMode = 0;
21393                     break;
21394             }
21395         }
21396                 
21397         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21398         
21399 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21400         
21401         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21402         
21403         this.picker().on('mousedown', this.onMousedown, this);
21404         this.picker().on('click', this.onClick, this);
21405         
21406         this.picker().addClass('datepicker-dropdown');
21407         
21408         this.startViewMode = this.viewMode;
21409         
21410         if(this.singleMode){
21411             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21412                 v.setVisibilityMode(Roo.Element.DISPLAY);
21413                 v.hide();
21414             });
21415             
21416             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21417                 v.setStyle('width', '189px');
21418             });
21419         }
21420         
21421         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21422             if(!this.calendarWeeks){
21423                 v.remove();
21424                 return;
21425             }
21426             
21427             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21428             v.attr('colspan', function(i, val){
21429                 return parseInt(val) + 1;
21430             });
21431         });
21432                         
21433         
21434         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21435         
21436         this.setStartDate(this.startDate);
21437         this.setEndDate(this.endDate);
21438         
21439         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21440         
21441         this.fillDow();
21442         this.fillMonths();
21443         this.update();
21444         this.showMode();
21445         
21446         if(this.isInline) {
21447             this.showPopup();
21448         }
21449     },
21450     
21451     picker : function()
21452     {
21453         return this.pickerEl;
21454 //        return this.el.select('.datepicker', true).first();
21455     },
21456     
21457     fillDow: function()
21458     {
21459         var dowCnt = this.weekStart;
21460         
21461         var dow = {
21462             tag: 'tr',
21463             cn: [
21464                 
21465             ]
21466         };
21467         
21468         if(this.calendarWeeks){
21469             dow.cn.push({
21470                 tag: 'th',
21471                 cls: 'cw',
21472                 html: '&nbsp;'
21473             })
21474         }
21475         
21476         while (dowCnt < this.weekStart + 7) {
21477             dow.cn.push({
21478                 tag: 'th',
21479                 cls: 'dow',
21480                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21481             });
21482         }
21483         
21484         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21485     },
21486     
21487     fillMonths: function()
21488     {    
21489         var i = 0;
21490         var months = this.picker().select('>.datepicker-months td', true).first();
21491         
21492         months.dom.innerHTML = '';
21493         
21494         while (i < 12) {
21495             var month = {
21496                 tag: 'span',
21497                 cls: 'month',
21498                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21499             };
21500             
21501             months.createChild(month);
21502         }
21503         
21504     },
21505     
21506     update: function()
21507     {
21508         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;
21509         
21510         if (this.date < this.startDate) {
21511             this.viewDate = new Date(this.startDate);
21512         } else if (this.date > this.endDate) {
21513             this.viewDate = new Date(this.endDate);
21514         } else {
21515             this.viewDate = new Date(this.date);
21516         }
21517         
21518         this.fill();
21519     },
21520     
21521     fill: function() 
21522     {
21523         var d = new Date(this.viewDate),
21524                 year = d.getUTCFullYear(),
21525                 month = d.getUTCMonth(),
21526                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21527                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21528                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21529                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21530                 currentDate = this.date && this.date.valueOf(),
21531                 today = this.UTCToday();
21532         
21533         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21534         
21535 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21536         
21537 //        this.picker.select('>tfoot th.today').
21538 //                                              .text(dates[this.language].today)
21539 //                                              .toggle(this.todayBtn !== false);
21540     
21541         this.updateNavArrows();
21542         this.fillMonths();
21543                                                 
21544         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21545         
21546         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21547          
21548         prevMonth.setUTCDate(day);
21549         
21550         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21551         
21552         var nextMonth = new Date(prevMonth);
21553         
21554         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21555         
21556         nextMonth = nextMonth.valueOf();
21557         
21558         var fillMonths = false;
21559         
21560         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21561         
21562         while(prevMonth.valueOf() <= nextMonth) {
21563             var clsName = '';
21564             
21565             if (prevMonth.getUTCDay() === this.weekStart) {
21566                 if(fillMonths){
21567                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21568                 }
21569                     
21570                 fillMonths = {
21571                     tag: 'tr',
21572                     cn: []
21573                 };
21574                 
21575                 if(this.calendarWeeks){
21576                     // ISO 8601: First week contains first thursday.
21577                     // ISO also states week starts on Monday, but we can be more abstract here.
21578                     var
21579                     // Start of current week: based on weekstart/current date
21580                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21581                     // Thursday of this week
21582                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21583                     // First Thursday of year, year from thursday
21584                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21585                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21586                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21587                     
21588                     fillMonths.cn.push({
21589                         tag: 'td',
21590                         cls: 'cw',
21591                         html: calWeek
21592                     });
21593                 }
21594             }
21595             
21596             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21597                 clsName += ' old';
21598             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21599                 clsName += ' new';
21600             }
21601             if (this.todayHighlight &&
21602                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21603                 prevMonth.getUTCMonth() == today.getMonth() &&
21604                 prevMonth.getUTCDate() == today.getDate()) {
21605                 clsName += ' today';
21606             }
21607             
21608             if (currentDate && prevMonth.valueOf() === currentDate) {
21609                 clsName += ' active';
21610             }
21611             
21612             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21613                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21614                     clsName += ' disabled';
21615             }
21616             
21617             fillMonths.cn.push({
21618                 tag: 'td',
21619                 cls: 'day ' + clsName,
21620                 html: prevMonth.getDate()
21621             });
21622             
21623             prevMonth.setDate(prevMonth.getDate()+1);
21624         }
21625           
21626         var currentYear = this.date && this.date.getUTCFullYear();
21627         var currentMonth = this.date && this.date.getUTCMonth();
21628         
21629         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21630         
21631         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21632             v.removeClass('active');
21633             
21634             if(currentYear === year && k === currentMonth){
21635                 v.addClass('active');
21636             }
21637             
21638             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21639                 v.addClass('disabled');
21640             }
21641             
21642         });
21643         
21644         
21645         year = parseInt(year/10, 10) * 10;
21646         
21647         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21648         
21649         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21650         
21651         year -= 1;
21652         for (var i = -1; i < 11; i++) {
21653             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21654                 tag: 'span',
21655                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21656                 html: year
21657             });
21658             
21659             year += 1;
21660         }
21661     },
21662     
21663     showMode: function(dir) 
21664     {
21665         if (dir) {
21666             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21667         }
21668         
21669         Roo.each(this.picker().select('>div',true).elements, function(v){
21670             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21671             v.hide();
21672         });
21673         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21674     },
21675     
21676     place: function()
21677     {
21678         if(this.isInline) {
21679             return;
21680         }
21681         
21682         this.picker().removeClass(['bottom', 'top']);
21683         
21684         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21685             /*
21686              * place to the top of element!
21687              *
21688              */
21689             
21690             this.picker().addClass('top');
21691             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21692             
21693             return;
21694         }
21695         
21696         this.picker().addClass('bottom');
21697         
21698         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21699     },
21700     
21701     parseDate : function(value)
21702     {
21703         if(!value || value instanceof Date){
21704             return value;
21705         }
21706         var v = Date.parseDate(value, this.format);
21707         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21708             v = Date.parseDate(value, 'Y-m-d');
21709         }
21710         if(!v && this.altFormats){
21711             if(!this.altFormatsArray){
21712                 this.altFormatsArray = this.altFormats.split("|");
21713             }
21714             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21715                 v = Date.parseDate(value, this.altFormatsArray[i]);
21716             }
21717         }
21718         return v;
21719     },
21720     
21721     formatDate : function(date, fmt)
21722     {   
21723         return (!date || !(date instanceof Date)) ?
21724         date : date.dateFormat(fmt || this.format);
21725     },
21726     
21727     onFocus : function()
21728     {
21729         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21730         this.showPopup();
21731     },
21732     
21733     onBlur : function()
21734     {
21735         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21736         
21737         var d = this.inputEl().getValue();
21738         
21739         this.setValue(d);
21740                 
21741         this.hidePopup();
21742     },
21743     
21744     showPopup : function()
21745     {
21746         this.picker().show();
21747         this.update();
21748         this.place();
21749         
21750         this.fireEvent('showpopup', this, this.date);
21751     },
21752     
21753     hidePopup : function()
21754     {
21755         if(this.isInline) {
21756             return;
21757         }
21758         this.picker().hide();
21759         this.viewMode = this.startViewMode;
21760         this.showMode();
21761         
21762         this.fireEvent('hidepopup', this, this.date);
21763         
21764     },
21765     
21766     onMousedown: function(e)
21767     {
21768         e.stopPropagation();
21769         e.preventDefault();
21770     },
21771     
21772     keyup: function(e)
21773     {
21774         Roo.bootstrap.DateField.superclass.keyup.call(this);
21775         this.update();
21776     },
21777
21778     setValue: function(v)
21779     {
21780         if(this.fireEvent('beforeselect', this, v) !== false){
21781             var d = new Date(this.parseDate(v) ).clearTime();
21782         
21783             if(isNaN(d.getTime())){
21784                 this.date = this.viewDate = '';
21785                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21786                 return;
21787             }
21788
21789             v = this.formatDate(d);
21790
21791             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21792
21793             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21794
21795             this.update();
21796
21797             this.fireEvent('select', this, this.date);
21798         }
21799     },
21800     
21801     getValue: function()
21802     {
21803         return this.formatDate(this.date);
21804     },
21805     
21806     fireKey: function(e)
21807     {
21808         if (!this.picker().isVisible()){
21809             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21810                 this.showPopup();
21811             }
21812             return;
21813         }
21814         
21815         var dateChanged = false,
21816         dir, day, month,
21817         newDate, newViewDate;
21818         
21819         switch(e.keyCode){
21820             case 27: // escape
21821                 this.hidePopup();
21822                 e.preventDefault();
21823                 break;
21824             case 37: // left
21825             case 39: // right
21826                 if (!this.keyboardNavigation) {
21827                     break;
21828                 }
21829                 dir = e.keyCode == 37 ? -1 : 1;
21830                 
21831                 if (e.ctrlKey){
21832                     newDate = this.moveYear(this.date, dir);
21833                     newViewDate = this.moveYear(this.viewDate, dir);
21834                 } else if (e.shiftKey){
21835                     newDate = this.moveMonth(this.date, dir);
21836                     newViewDate = this.moveMonth(this.viewDate, dir);
21837                 } else {
21838                     newDate = new Date(this.date);
21839                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21840                     newViewDate = new Date(this.viewDate);
21841                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21842                 }
21843                 if (this.dateWithinRange(newDate)){
21844                     this.date = newDate;
21845                     this.viewDate = newViewDate;
21846                     this.setValue(this.formatDate(this.date));
21847 //                    this.update();
21848                     e.preventDefault();
21849                     dateChanged = true;
21850                 }
21851                 break;
21852             case 38: // up
21853             case 40: // down
21854                 if (!this.keyboardNavigation) {
21855                     break;
21856                 }
21857                 dir = e.keyCode == 38 ? -1 : 1;
21858                 if (e.ctrlKey){
21859                     newDate = this.moveYear(this.date, dir);
21860                     newViewDate = this.moveYear(this.viewDate, dir);
21861                 } else if (e.shiftKey){
21862                     newDate = this.moveMonth(this.date, dir);
21863                     newViewDate = this.moveMonth(this.viewDate, dir);
21864                 } else {
21865                     newDate = new Date(this.date);
21866                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21867                     newViewDate = new Date(this.viewDate);
21868                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21869                 }
21870                 if (this.dateWithinRange(newDate)){
21871                     this.date = newDate;
21872                     this.viewDate = newViewDate;
21873                     this.setValue(this.formatDate(this.date));
21874 //                    this.update();
21875                     e.preventDefault();
21876                     dateChanged = true;
21877                 }
21878                 break;
21879             case 13: // enter
21880                 this.setValue(this.formatDate(this.date));
21881                 this.hidePopup();
21882                 e.preventDefault();
21883                 break;
21884             case 9: // tab
21885                 this.setValue(this.formatDate(this.date));
21886                 this.hidePopup();
21887                 break;
21888             case 16: // shift
21889             case 17: // ctrl
21890             case 18: // alt
21891                 break;
21892             default :
21893                 this.hidePopup();
21894                 
21895         }
21896     },
21897     
21898     
21899     onClick: function(e) 
21900     {
21901         e.stopPropagation();
21902         e.preventDefault();
21903         
21904         var target = e.getTarget();
21905         
21906         if(target.nodeName.toLowerCase() === 'i'){
21907             target = Roo.get(target).dom.parentNode;
21908         }
21909         
21910         var nodeName = target.nodeName;
21911         var className = target.className;
21912         var html = target.innerHTML;
21913         //Roo.log(nodeName);
21914         
21915         switch(nodeName.toLowerCase()) {
21916             case 'th':
21917                 switch(className) {
21918                     case 'switch':
21919                         this.showMode(1);
21920                         break;
21921                     case 'prev':
21922                     case 'next':
21923                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21924                         switch(this.viewMode){
21925                                 case 0:
21926                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21927                                         break;
21928                                 case 1:
21929                                 case 2:
21930                                         this.viewDate = this.moveYear(this.viewDate, dir);
21931                                         break;
21932                         }
21933                         this.fill();
21934                         break;
21935                     case 'today':
21936                         var date = new Date();
21937                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21938 //                        this.fill()
21939                         this.setValue(this.formatDate(this.date));
21940                         
21941                         this.hidePopup();
21942                         break;
21943                 }
21944                 break;
21945             case 'span':
21946                 if (className.indexOf('disabled') < 0) {
21947                     this.viewDate.setUTCDate(1);
21948                     if (className.indexOf('month') > -1) {
21949                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21950                     } else {
21951                         var year = parseInt(html, 10) || 0;
21952                         this.viewDate.setUTCFullYear(year);
21953                         
21954                     }
21955                     
21956                     if(this.singleMode){
21957                         this.setValue(this.formatDate(this.viewDate));
21958                         this.hidePopup();
21959                         return;
21960                     }
21961                     
21962                     this.showMode(-1);
21963                     this.fill();
21964                 }
21965                 break;
21966                 
21967             case 'td':
21968                 //Roo.log(className);
21969                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21970                     var day = parseInt(html, 10) || 1;
21971                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21972                         month = (this.viewDate || new Date()).getUTCMonth();
21973
21974                     if (className.indexOf('old') > -1) {
21975                         if(month === 0 ){
21976                             month = 11;
21977                             year -= 1;
21978                         }else{
21979                             month -= 1;
21980                         }
21981                     } else if (className.indexOf('new') > -1) {
21982                         if (month == 11) {
21983                             month = 0;
21984                             year += 1;
21985                         } else {
21986                             month += 1;
21987                         }
21988                     }
21989                     //Roo.log([year,month,day]);
21990                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21991                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21992 //                    this.fill();
21993                     //Roo.log(this.formatDate(this.date));
21994                     this.setValue(this.formatDate(this.date));
21995                     this.hidePopup();
21996                 }
21997                 break;
21998         }
21999     },
22000     
22001     setStartDate: function(startDate)
22002     {
22003         this.startDate = startDate || -Infinity;
22004         if (this.startDate !== -Infinity) {
22005             this.startDate = this.parseDate(this.startDate);
22006         }
22007         this.update();
22008         this.updateNavArrows();
22009     },
22010
22011     setEndDate: function(endDate)
22012     {
22013         this.endDate = endDate || Infinity;
22014         if (this.endDate !== Infinity) {
22015             this.endDate = this.parseDate(this.endDate);
22016         }
22017         this.update();
22018         this.updateNavArrows();
22019     },
22020     
22021     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22022     {
22023         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22024         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22025             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22026         }
22027         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22028             return parseInt(d, 10);
22029         });
22030         this.update();
22031         this.updateNavArrows();
22032     },
22033     
22034     updateNavArrows: function() 
22035     {
22036         if(this.singleMode){
22037             return;
22038         }
22039         
22040         var d = new Date(this.viewDate),
22041         year = d.getUTCFullYear(),
22042         month = d.getUTCMonth();
22043         
22044         Roo.each(this.picker().select('.prev', true).elements, function(v){
22045             v.show();
22046             switch (this.viewMode) {
22047                 case 0:
22048
22049                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22050                         v.hide();
22051                     }
22052                     break;
22053                 case 1:
22054                 case 2:
22055                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22056                         v.hide();
22057                     }
22058                     break;
22059             }
22060         });
22061         
22062         Roo.each(this.picker().select('.next', true).elements, function(v){
22063             v.show();
22064             switch (this.viewMode) {
22065                 case 0:
22066
22067                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22068                         v.hide();
22069                     }
22070                     break;
22071                 case 1:
22072                 case 2:
22073                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22074                         v.hide();
22075                     }
22076                     break;
22077             }
22078         })
22079     },
22080     
22081     moveMonth: function(date, dir)
22082     {
22083         if (!dir) {
22084             return date;
22085         }
22086         var new_date = new Date(date.valueOf()),
22087         day = new_date.getUTCDate(),
22088         month = new_date.getUTCMonth(),
22089         mag = Math.abs(dir),
22090         new_month, test;
22091         dir = dir > 0 ? 1 : -1;
22092         if (mag == 1){
22093             test = dir == -1
22094             // If going back one month, make sure month is not current month
22095             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22096             ? function(){
22097                 return new_date.getUTCMonth() == month;
22098             }
22099             // If going forward one month, make sure month is as expected
22100             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22101             : function(){
22102                 return new_date.getUTCMonth() != new_month;
22103             };
22104             new_month = month + dir;
22105             new_date.setUTCMonth(new_month);
22106             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22107             if (new_month < 0 || new_month > 11) {
22108                 new_month = (new_month + 12) % 12;
22109             }
22110         } else {
22111             // For magnitudes >1, move one month at a time...
22112             for (var i=0; i<mag; i++) {
22113                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22114                 new_date = this.moveMonth(new_date, dir);
22115             }
22116             // ...then reset the day, keeping it in the new month
22117             new_month = new_date.getUTCMonth();
22118             new_date.setUTCDate(day);
22119             test = function(){
22120                 return new_month != new_date.getUTCMonth();
22121             };
22122         }
22123         // Common date-resetting loop -- if date is beyond end of month, make it
22124         // end of month
22125         while (test()){
22126             new_date.setUTCDate(--day);
22127             new_date.setUTCMonth(new_month);
22128         }
22129         return new_date;
22130     },
22131
22132     moveYear: function(date, dir)
22133     {
22134         return this.moveMonth(date, dir*12);
22135     },
22136
22137     dateWithinRange: function(date)
22138     {
22139         return date >= this.startDate && date <= this.endDate;
22140     },
22141
22142     
22143     remove: function() 
22144     {
22145         this.picker().remove();
22146     },
22147     
22148     validateValue : function(value)
22149     {
22150         if(this.getVisibilityEl().hasClass('hidden')){
22151             return true;
22152         }
22153         
22154         if(value.length < 1)  {
22155             if(this.allowBlank){
22156                 return true;
22157             }
22158             return false;
22159         }
22160         
22161         if(value.length < this.minLength){
22162             return false;
22163         }
22164         if(value.length > this.maxLength){
22165             return false;
22166         }
22167         if(this.vtype){
22168             var vt = Roo.form.VTypes;
22169             if(!vt[this.vtype](value, this)){
22170                 return false;
22171             }
22172         }
22173         if(typeof this.validator == "function"){
22174             var msg = this.validator(value);
22175             if(msg !== true){
22176                 return false;
22177             }
22178         }
22179         
22180         if(this.regex && !this.regex.test(value)){
22181             return false;
22182         }
22183         
22184         if(typeof(this.parseDate(value)) == 'undefined'){
22185             return false;
22186         }
22187         
22188         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22189             return false;
22190         }      
22191         
22192         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22193             return false;
22194         } 
22195         
22196         
22197         return true;
22198     },
22199     
22200     reset : function()
22201     {
22202         this.date = this.viewDate = '';
22203         
22204         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22205     }
22206    
22207 });
22208
22209 Roo.apply(Roo.bootstrap.DateField,  {
22210     
22211     head : {
22212         tag: 'thead',
22213         cn: [
22214         {
22215             tag: 'tr',
22216             cn: [
22217             {
22218                 tag: 'th',
22219                 cls: 'prev',
22220                 html: '<i class="fa fa-arrow-left"/>'
22221             },
22222             {
22223                 tag: 'th',
22224                 cls: 'switch',
22225                 colspan: '5'
22226             },
22227             {
22228                 tag: 'th',
22229                 cls: 'next',
22230                 html: '<i class="fa fa-arrow-right"/>'
22231             }
22232
22233             ]
22234         }
22235         ]
22236     },
22237     
22238     content : {
22239         tag: 'tbody',
22240         cn: [
22241         {
22242             tag: 'tr',
22243             cn: [
22244             {
22245                 tag: 'td',
22246                 colspan: '7'
22247             }
22248             ]
22249         }
22250         ]
22251     },
22252     
22253     footer : {
22254         tag: 'tfoot',
22255         cn: [
22256         {
22257             tag: 'tr',
22258             cn: [
22259             {
22260                 tag: 'th',
22261                 colspan: '7',
22262                 cls: 'today'
22263             }
22264                     
22265             ]
22266         }
22267         ]
22268     },
22269     
22270     dates:{
22271         en: {
22272             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22273             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22274             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22275             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22276             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22277             today: "Today"
22278         }
22279     },
22280     
22281     modes: [
22282     {
22283         clsName: 'days',
22284         navFnc: 'Month',
22285         navStep: 1
22286     },
22287     {
22288         clsName: 'months',
22289         navFnc: 'FullYear',
22290         navStep: 1
22291     },
22292     {
22293         clsName: 'years',
22294         navFnc: 'FullYear',
22295         navStep: 10
22296     }]
22297 });
22298
22299 Roo.apply(Roo.bootstrap.DateField,  {
22300   
22301     template : {
22302         tag: 'div',
22303         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22304         cn: [
22305         {
22306             tag: 'div',
22307             cls: 'datepicker-days',
22308             cn: [
22309             {
22310                 tag: 'table',
22311                 cls: 'table-condensed',
22312                 cn:[
22313                 Roo.bootstrap.DateField.head,
22314                 {
22315                     tag: 'tbody'
22316                 },
22317                 Roo.bootstrap.DateField.footer
22318                 ]
22319             }
22320             ]
22321         },
22322         {
22323             tag: 'div',
22324             cls: 'datepicker-months',
22325             cn: [
22326             {
22327                 tag: 'table',
22328                 cls: 'table-condensed',
22329                 cn:[
22330                 Roo.bootstrap.DateField.head,
22331                 Roo.bootstrap.DateField.content,
22332                 Roo.bootstrap.DateField.footer
22333                 ]
22334             }
22335             ]
22336         },
22337         {
22338             tag: 'div',
22339             cls: 'datepicker-years',
22340             cn: [
22341             {
22342                 tag: 'table',
22343                 cls: 'table-condensed',
22344                 cn:[
22345                 Roo.bootstrap.DateField.head,
22346                 Roo.bootstrap.DateField.content,
22347                 Roo.bootstrap.DateField.footer
22348                 ]
22349             }
22350             ]
22351         }
22352         ]
22353     }
22354 });
22355
22356  
22357
22358  /*
22359  * - LGPL
22360  *
22361  * TimeField
22362  * 
22363  */
22364
22365 /**
22366  * @class Roo.bootstrap.TimeField
22367  * @extends Roo.bootstrap.Input
22368  * Bootstrap DateField class
22369  * 
22370  * 
22371  * @constructor
22372  * Create a new TimeField
22373  * @param {Object} config The config object
22374  */
22375
22376 Roo.bootstrap.TimeField = function(config){
22377     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22378     this.addEvents({
22379             /**
22380              * @event show
22381              * Fires when this field show.
22382              * @param {Roo.bootstrap.DateField} thisthis
22383              * @param {Mixed} date The date value
22384              */
22385             show : true,
22386             /**
22387              * @event show
22388              * Fires when this field hide.
22389              * @param {Roo.bootstrap.DateField} this
22390              * @param {Mixed} date The date value
22391              */
22392             hide : true,
22393             /**
22394              * @event select
22395              * Fires when select a date.
22396              * @param {Roo.bootstrap.DateField} this
22397              * @param {Mixed} date The date value
22398              */
22399             select : true
22400         });
22401 };
22402
22403 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22404     
22405     /**
22406      * @cfg {String} format
22407      * The default time format string which can be overriden for localization support.  The format must be
22408      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22409      */
22410     format : "H:i",
22411
22412     getAutoCreate : function()
22413     {
22414         this.after = '<i class="fa far fa-clock"></i>';
22415         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22416         
22417          
22418     },
22419     onRender: function(ct, position)
22420     {
22421         
22422         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22423                 
22424         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22425         
22426         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22427         
22428         this.pop = this.picker().select('>.datepicker-time',true).first();
22429         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22430         
22431         this.picker().on('mousedown', this.onMousedown, this);
22432         this.picker().on('click', this.onClick, this);
22433         
22434         this.picker().addClass('datepicker-dropdown');
22435     
22436         this.fillTime();
22437         this.update();
22438             
22439         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22440         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22441         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22442         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22443         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22444         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22445
22446     },
22447     
22448     fireKey: function(e){
22449         if (!this.picker().isVisible()){
22450             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22451                 this.show();
22452             }
22453             return;
22454         }
22455
22456         e.preventDefault();
22457         
22458         switch(e.keyCode){
22459             case 27: // escape
22460                 this.hide();
22461                 break;
22462             case 37: // left
22463             case 39: // right
22464                 this.onTogglePeriod();
22465                 break;
22466             case 38: // up
22467                 this.onIncrementMinutes();
22468                 break;
22469             case 40: // down
22470                 this.onDecrementMinutes();
22471                 break;
22472             case 13: // enter
22473             case 9: // tab
22474                 this.setTime();
22475                 break;
22476         }
22477     },
22478     
22479     onClick: function(e) {
22480         e.stopPropagation();
22481         e.preventDefault();
22482     },
22483     
22484     picker : function()
22485     {
22486         return this.pickerEl;
22487     },
22488     
22489     fillTime: function()
22490     {    
22491         var time = this.pop.select('tbody', true).first();
22492         
22493         time.dom.innerHTML = '';
22494         
22495         time.createChild({
22496             tag: 'tr',
22497             cn: [
22498                 {
22499                     tag: 'td',
22500                     cn: [
22501                         {
22502                             tag: 'a',
22503                             href: '#',
22504                             cls: 'btn',
22505                             cn: [
22506                                 {
22507                                     tag: 'i',
22508                                     cls: 'hours-up fa fas fa-chevron-up'
22509                                 }
22510                             ]
22511                         } 
22512                     ]
22513                 },
22514                 {
22515                     tag: 'td',
22516                     cls: 'separator'
22517                 },
22518                 {
22519                     tag: 'td',
22520                     cn: [
22521                         {
22522                             tag: 'a',
22523                             href: '#',
22524                             cls: 'btn',
22525                             cn: [
22526                                 {
22527                                     tag: 'i',
22528                                     cls: 'minutes-up fa fas fa-chevron-up'
22529                                 }
22530                             ]
22531                         }
22532                     ]
22533                 },
22534                 {
22535                     tag: 'td',
22536                     cls: 'separator'
22537                 }
22538             ]
22539         });
22540         
22541         time.createChild({
22542             tag: 'tr',
22543             cn: [
22544                 {
22545                     tag: 'td',
22546                     cn: [
22547                         {
22548                             tag: 'span',
22549                             cls: 'timepicker-hour',
22550                             html: '00'
22551                         }  
22552                     ]
22553                 },
22554                 {
22555                     tag: 'td',
22556                     cls: 'separator',
22557                     html: ':'
22558                 },
22559                 {
22560                     tag: 'td',
22561                     cn: [
22562                         {
22563                             tag: 'span',
22564                             cls: 'timepicker-minute',
22565                             html: '00'
22566                         }  
22567                     ]
22568                 },
22569                 {
22570                     tag: 'td',
22571                     cls: 'separator'
22572                 },
22573                 {
22574                     tag: 'td',
22575                     cn: [
22576                         {
22577                             tag: 'button',
22578                             type: 'button',
22579                             cls: 'btn btn-primary period',
22580                             html: 'AM'
22581                             
22582                         }
22583                     ]
22584                 }
22585             ]
22586         });
22587         
22588         time.createChild({
22589             tag: 'tr',
22590             cn: [
22591                 {
22592                     tag: 'td',
22593                     cn: [
22594                         {
22595                             tag: 'a',
22596                             href: '#',
22597                             cls: 'btn',
22598                             cn: [
22599                                 {
22600                                     tag: 'span',
22601                                     cls: 'hours-down fa fas fa-chevron-down'
22602                                 }
22603                             ]
22604                         }
22605                     ]
22606                 },
22607                 {
22608                     tag: 'td',
22609                     cls: 'separator'
22610                 },
22611                 {
22612                     tag: 'td',
22613                     cn: [
22614                         {
22615                             tag: 'a',
22616                             href: '#',
22617                             cls: 'btn',
22618                             cn: [
22619                                 {
22620                                     tag: 'span',
22621                                     cls: 'minutes-down fa fas fa-chevron-down'
22622                                 }
22623                             ]
22624                         }
22625                     ]
22626                 },
22627                 {
22628                     tag: 'td',
22629                     cls: 'separator'
22630                 }
22631             ]
22632         });
22633         
22634     },
22635     
22636     update: function()
22637     {
22638         
22639         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22640         
22641         this.fill();
22642     },
22643     
22644     fill: function() 
22645     {
22646         var hours = this.time.getHours();
22647         var minutes = this.time.getMinutes();
22648         var period = 'AM';
22649         
22650         if(hours > 11){
22651             period = 'PM';
22652         }
22653         
22654         if(hours == 0){
22655             hours = 12;
22656         }
22657         
22658         
22659         if(hours > 12){
22660             hours = hours - 12;
22661         }
22662         
22663         if(hours < 10){
22664             hours = '0' + hours;
22665         }
22666         
22667         if(minutes < 10){
22668             minutes = '0' + minutes;
22669         }
22670         
22671         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22672         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22673         this.pop.select('button', true).first().dom.innerHTML = period;
22674         
22675     },
22676     
22677     place: function()
22678     {   
22679         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22680         
22681         var cls = ['bottom'];
22682         
22683         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22684             cls.pop();
22685             cls.push('top');
22686         }
22687         
22688         cls.push('right');
22689         
22690         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22691             cls.pop();
22692             cls.push('left');
22693         }
22694         //this.picker().setXY(20000,20000);
22695         this.picker().addClass(cls.join('-'));
22696         
22697         var _this = this;
22698         
22699         Roo.each(cls, function(c){
22700             if(c == 'bottom'){
22701                 (function() {
22702                  //  
22703                 }).defer(200);
22704                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22705                 //_this.picker().setTop(_this.inputEl().getHeight());
22706                 return;
22707             }
22708             if(c == 'top'){
22709                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22710                 
22711                 //_this.picker().setTop(0 - _this.picker().getHeight());
22712                 return;
22713             }
22714             /*
22715             if(c == 'left'){
22716                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22717                 return;
22718             }
22719             if(c == 'right'){
22720                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22721                 return;
22722             }
22723             */
22724         });
22725         
22726     },
22727   
22728     onFocus : function()
22729     {
22730         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22731         this.show();
22732     },
22733     
22734     onBlur : function()
22735     {
22736         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22737         this.hide();
22738     },
22739     
22740     show : function()
22741     {
22742         this.picker().show();
22743         this.pop.show();
22744         this.update();
22745         this.place();
22746         
22747         this.fireEvent('show', this, this.date);
22748     },
22749     
22750     hide : function()
22751     {
22752         this.picker().hide();
22753         this.pop.hide();
22754         
22755         this.fireEvent('hide', this, this.date);
22756     },
22757     
22758     setTime : function()
22759     {
22760         this.hide();
22761         this.setValue(this.time.format(this.format));
22762         
22763         this.fireEvent('select', this, this.date);
22764         
22765         
22766     },
22767     
22768     onMousedown: function(e){
22769         e.stopPropagation();
22770         e.preventDefault();
22771     },
22772     
22773     onIncrementHours: function()
22774     {
22775         Roo.log('onIncrementHours');
22776         this.time = this.time.add(Date.HOUR, 1);
22777         this.update();
22778         
22779     },
22780     
22781     onDecrementHours: function()
22782     {
22783         Roo.log('onDecrementHours');
22784         this.time = this.time.add(Date.HOUR, -1);
22785         this.update();
22786     },
22787     
22788     onIncrementMinutes: function()
22789     {
22790         Roo.log('onIncrementMinutes');
22791         this.time = this.time.add(Date.MINUTE, 1);
22792         this.update();
22793     },
22794     
22795     onDecrementMinutes: function()
22796     {
22797         Roo.log('onDecrementMinutes');
22798         this.time = this.time.add(Date.MINUTE, -1);
22799         this.update();
22800     },
22801     
22802     onTogglePeriod: function()
22803     {
22804         Roo.log('onTogglePeriod');
22805         this.time = this.time.add(Date.HOUR, 12);
22806         this.update();
22807     }
22808     
22809    
22810 });
22811  
22812
22813 Roo.apply(Roo.bootstrap.TimeField,  {
22814   
22815     template : {
22816         tag: 'div',
22817         cls: 'datepicker dropdown-menu',
22818         cn: [
22819             {
22820                 tag: 'div',
22821                 cls: 'datepicker-time',
22822                 cn: [
22823                 {
22824                     tag: 'table',
22825                     cls: 'table-condensed',
22826                     cn:[
22827                         {
22828                             tag: 'tbody',
22829                             cn: [
22830                                 {
22831                                     tag: 'tr',
22832                                     cn: [
22833                                     {
22834                                         tag: 'td',
22835                                         colspan: '7'
22836                                     }
22837                                     ]
22838                                 }
22839                             ]
22840                         },
22841                         {
22842                             tag: 'tfoot',
22843                             cn: [
22844                                 {
22845                                     tag: 'tr',
22846                                     cn: [
22847                                     {
22848                                         tag: 'th',
22849                                         colspan: '7',
22850                                         cls: '',
22851                                         cn: [
22852                                             {
22853                                                 tag: 'button',
22854                                                 cls: 'btn btn-info ok',
22855                                                 html: 'OK'
22856                                             }
22857                                         ]
22858                                     }
22859                     
22860                                     ]
22861                                 }
22862                             ]
22863                         }
22864                     ]
22865                 }
22866                 ]
22867             }
22868         ]
22869     }
22870 });
22871
22872  
22873
22874  /*
22875  * - LGPL
22876  *
22877  * MonthField
22878  * 
22879  */
22880
22881 /**
22882  * @class Roo.bootstrap.MonthField
22883  * @extends Roo.bootstrap.Input
22884  * Bootstrap MonthField class
22885  * 
22886  * @cfg {String} language default en
22887  * 
22888  * @constructor
22889  * Create a new MonthField
22890  * @param {Object} config The config object
22891  */
22892
22893 Roo.bootstrap.MonthField = function(config){
22894     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22895     
22896     this.addEvents({
22897         /**
22898          * @event show
22899          * Fires when this field show.
22900          * @param {Roo.bootstrap.MonthField} this
22901          * @param {Mixed} date The date value
22902          */
22903         show : true,
22904         /**
22905          * @event show
22906          * Fires when this field hide.
22907          * @param {Roo.bootstrap.MonthField} this
22908          * @param {Mixed} date The date value
22909          */
22910         hide : true,
22911         /**
22912          * @event select
22913          * Fires when select a date.
22914          * @param {Roo.bootstrap.MonthField} this
22915          * @param {String} oldvalue The old value
22916          * @param {String} newvalue The new value
22917          */
22918         select : true
22919     });
22920 };
22921
22922 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22923     
22924     onRender: function(ct, position)
22925     {
22926         
22927         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22928         
22929         this.language = this.language || 'en';
22930         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22931         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22932         
22933         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22934         this.isInline = false;
22935         this.isInput = true;
22936         this.component = this.el.select('.add-on', true).first() || false;
22937         this.component = (this.component && this.component.length === 0) ? false : this.component;
22938         this.hasInput = this.component && this.inputEL().length;
22939         
22940         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22941         
22942         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22943         
22944         this.picker().on('mousedown', this.onMousedown, this);
22945         this.picker().on('click', this.onClick, this);
22946         
22947         this.picker().addClass('datepicker-dropdown');
22948         
22949         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22950             v.setStyle('width', '189px');
22951         });
22952         
22953         this.fillMonths();
22954         
22955         this.update();
22956         
22957         if(this.isInline) {
22958             this.show();
22959         }
22960         
22961     },
22962     
22963     setValue: function(v, suppressEvent)
22964     {   
22965         var o = this.getValue();
22966         
22967         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22968         
22969         this.update();
22970
22971         if(suppressEvent !== true){
22972             this.fireEvent('select', this, o, v);
22973         }
22974         
22975     },
22976     
22977     getValue: function()
22978     {
22979         return this.value;
22980     },
22981     
22982     onClick: function(e) 
22983     {
22984         e.stopPropagation();
22985         e.preventDefault();
22986         
22987         var target = e.getTarget();
22988         
22989         if(target.nodeName.toLowerCase() === 'i'){
22990             target = Roo.get(target).dom.parentNode;
22991         }
22992         
22993         var nodeName = target.nodeName;
22994         var className = target.className;
22995         var html = target.innerHTML;
22996         
22997         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22998             return;
22999         }
23000         
23001         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23002         
23003         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23004         
23005         this.hide();
23006                         
23007     },
23008     
23009     picker : function()
23010     {
23011         return this.pickerEl;
23012     },
23013     
23014     fillMonths: function()
23015     {    
23016         var i = 0;
23017         var months = this.picker().select('>.datepicker-months td', true).first();
23018         
23019         months.dom.innerHTML = '';
23020         
23021         while (i < 12) {
23022             var month = {
23023                 tag: 'span',
23024                 cls: 'month',
23025                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23026             };
23027             
23028             months.createChild(month);
23029         }
23030         
23031     },
23032     
23033     update: function()
23034     {
23035         var _this = this;
23036         
23037         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23038             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23039         }
23040         
23041         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23042             e.removeClass('active');
23043             
23044             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23045                 e.addClass('active');
23046             }
23047         })
23048     },
23049     
23050     place: function()
23051     {
23052         if(this.isInline) {
23053             return;
23054         }
23055         
23056         this.picker().removeClass(['bottom', 'top']);
23057         
23058         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23059             /*
23060              * place to the top of element!
23061              *
23062              */
23063             
23064             this.picker().addClass('top');
23065             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23066             
23067             return;
23068         }
23069         
23070         this.picker().addClass('bottom');
23071         
23072         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23073     },
23074     
23075     onFocus : function()
23076     {
23077         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23078         this.show();
23079     },
23080     
23081     onBlur : function()
23082     {
23083         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23084         
23085         var d = this.inputEl().getValue();
23086         
23087         this.setValue(d);
23088                 
23089         this.hide();
23090     },
23091     
23092     show : function()
23093     {
23094         this.picker().show();
23095         this.picker().select('>.datepicker-months', true).first().show();
23096         this.update();
23097         this.place();
23098         
23099         this.fireEvent('show', this, this.date);
23100     },
23101     
23102     hide : function()
23103     {
23104         if(this.isInline) {
23105             return;
23106         }
23107         this.picker().hide();
23108         this.fireEvent('hide', this, this.date);
23109         
23110     },
23111     
23112     onMousedown: function(e)
23113     {
23114         e.stopPropagation();
23115         e.preventDefault();
23116     },
23117     
23118     keyup: function(e)
23119     {
23120         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23121         this.update();
23122     },
23123
23124     fireKey: function(e)
23125     {
23126         if (!this.picker().isVisible()){
23127             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23128                 this.show();
23129             }
23130             return;
23131         }
23132         
23133         var dir;
23134         
23135         switch(e.keyCode){
23136             case 27: // escape
23137                 this.hide();
23138                 e.preventDefault();
23139                 break;
23140             case 37: // left
23141             case 39: // right
23142                 dir = e.keyCode == 37 ? -1 : 1;
23143                 
23144                 this.vIndex = this.vIndex + dir;
23145                 
23146                 if(this.vIndex < 0){
23147                     this.vIndex = 0;
23148                 }
23149                 
23150                 if(this.vIndex > 11){
23151                     this.vIndex = 11;
23152                 }
23153                 
23154                 if(isNaN(this.vIndex)){
23155                     this.vIndex = 0;
23156                 }
23157                 
23158                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23159                 
23160                 break;
23161             case 38: // up
23162             case 40: // down
23163                 
23164                 dir = e.keyCode == 38 ? -1 : 1;
23165                 
23166                 this.vIndex = this.vIndex + dir * 4;
23167                 
23168                 if(this.vIndex < 0){
23169                     this.vIndex = 0;
23170                 }
23171                 
23172                 if(this.vIndex > 11){
23173                     this.vIndex = 11;
23174                 }
23175                 
23176                 if(isNaN(this.vIndex)){
23177                     this.vIndex = 0;
23178                 }
23179                 
23180                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23181                 break;
23182                 
23183             case 13: // enter
23184                 
23185                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23186                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23187                 }
23188                 
23189                 this.hide();
23190                 e.preventDefault();
23191                 break;
23192             case 9: // tab
23193                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23194                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23195                 }
23196                 this.hide();
23197                 break;
23198             case 16: // shift
23199             case 17: // ctrl
23200             case 18: // alt
23201                 break;
23202             default :
23203                 this.hide();
23204                 
23205         }
23206     },
23207     
23208     remove: function() 
23209     {
23210         this.picker().remove();
23211     }
23212    
23213 });
23214
23215 Roo.apply(Roo.bootstrap.MonthField,  {
23216     
23217     content : {
23218         tag: 'tbody',
23219         cn: [
23220         {
23221             tag: 'tr',
23222             cn: [
23223             {
23224                 tag: 'td',
23225                 colspan: '7'
23226             }
23227             ]
23228         }
23229         ]
23230     },
23231     
23232     dates:{
23233         en: {
23234             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23235             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23236         }
23237     }
23238 });
23239
23240 Roo.apply(Roo.bootstrap.MonthField,  {
23241   
23242     template : {
23243         tag: 'div',
23244         cls: 'datepicker dropdown-menu roo-dynamic',
23245         cn: [
23246             {
23247                 tag: 'div',
23248                 cls: 'datepicker-months',
23249                 cn: [
23250                 {
23251                     tag: 'table',
23252                     cls: 'table-condensed',
23253                     cn:[
23254                         Roo.bootstrap.DateField.content
23255                     ]
23256                 }
23257                 ]
23258             }
23259         ]
23260     }
23261 });
23262
23263  
23264
23265  
23266  /*
23267  * - LGPL
23268  *
23269  * CheckBox
23270  * 
23271  */
23272
23273 /**
23274  * @class Roo.bootstrap.CheckBox
23275  * @extends Roo.bootstrap.Input
23276  * Bootstrap CheckBox class
23277  * 
23278  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23279  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23280  * @cfg {String} boxLabel The text that appears beside the checkbox
23281  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23282  * @cfg {Boolean} checked initnal the element
23283  * @cfg {Boolean} inline inline the element (default false)
23284  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23285  * @cfg {String} tooltip label tooltip
23286  * 
23287  * @constructor
23288  * Create a new CheckBox
23289  * @param {Object} config The config object
23290  */
23291
23292 Roo.bootstrap.CheckBox = function(config){
23293     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23294    
23295     this.addEvents({
23296         /**
23297         * @event check
23298         * Fires when the element is checked or unchecked.
23299         * @param {Roo.bootstrap.CheckBox} this This input
23300         * @param {Boolean} checked The new checked value
23301         */
23302        check : true,
23303        /**
23304         * @event click
23305         * Fires when the element is click.
23306         * @param {Roo.bootstrap.CheckBox} this This input
23307         */
23308        click : true
23309     });
23310     
23311 };
23312
23313 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23314   
23315     inputType: 'checkbox',
23316     inputValue: 1,
23317     valueOff: 0,
23318     boxLabel: false,
23319     checked: false,
23320     weight : false,
23321     inline: false,
23322     tooltip : '',
23323     
23324     // checkbox success does not make any sense really.. 
23325     invalidClass : "",
23326     validClass : "",
23327     
23328     
23329     getAutoCreate : function()
23330     {
23331         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23332         
23333         var id = Roo.id();
23334         
23335         var cfg = {};
23336         
23337         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23338         
23339         if(this.inline){
23340             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23341         }
23342         
23343         var input =  {
23344             tag: 'input',
23345             id : id,
23346             type : this.inputType,
23347             value : this.inputValue,
23348             cls : 'roo-' + this.inputType, //'form-box',
23349             placeholder : this.placeholder || ''
23350             
23351         };
23352         
23353         if(this.inputType != 'radio'){
23354             var hidden =  {
23355                 tag: 'input',
23356                 type : 'hidden',
23357                 cls : 'roo-hidden-value',
23358                 value : this.checked ? this.inputValue : this.valueOff
23359             };
23360         }
23361         
23362             
23363         if (this.weight) { // Validity check?
23364             cfg.cls += " " + this.inputType + "-" + this.weight;
23365         }
23366         
23367         if (this.disabled) {
23368             input.disabled=true;
23369         }
23370         
23371         if(this.checked){
23372             input.checked = this.checked;
23373         }
23374         
23375         if (this.name) {
23376             
23377             input.name = this.name;
23378             
23379             if(this.inputType != 'radio'){
23380                 hidden.name = this.name;
23381                 input.name = '_hidden_' + this.name;
23382             }
23383         }
23384         
23385         if (this.size) {
23386             input.cls += ' input-' + this.size;
23387         }
23388         
23389         var settings=this;
23390         
23391         ['xs','sm','md','lg'].map(function(size){
23392             if (settings[size]) {
23393                 cfg.cls += ' col-' + size + '-' + settings[size];
23394             }
23395         });
23396         
23397         var inputblock = input;
23398          
23399         if (this.before || this.after) {
23400             
23401             inputblock = {
23402                 cls : 'input-group',
23403                 cn :  [] 
23404             };
23405             
23406             if (this.before) {
23407                 inputblock.cn.push({
23408                     tag :'span',
23409                     cls : 'input-group-addon',
23410                     html : this.before
23411                 });
23412             }
23413             
23414             inputblock.cn.push(input);
23415             
23416             if(this.inputType != 'radio'){
23417                 inputblock.cn.push(hidden);
23418             }
23419             
23420             if (this.after) {
23421                 inputblock.cn.push({
23422                     tag :'span',
23423                     cls : 'input-group-addon',
23424                     html : this.after
23425                 });
23426             }
23427             
23428         }
23429         var boxLabelCfg = false;
23430         
23431         if(this.boxLabel){
23432            
23433             boxLabelCfg = {
23434                 tag: 'label',
23435                 //'for': id, // box label is handled by onclick - so no for...
23436                 cls: 'box-label',
23437                 html: this.boxLabel
23438             };
23439             if(this.tooltip){
23440                 boxLabelCfg.tooltip = this.tooltip;
23441             }
23442              
23443         }
23444         
23445         
23446         if (align ==='left' && this.fieldLabel.length) {
23447 //                Roo.log("left and has label");
23448             cfg.cn = [
23449                 {
23450                     tag: 'label',
23451                     'for' :  id,
23452                     cls : 'control-label',
23453                     html : this.fieldLabel
23454                 },
23455                 {
23456                     cls : "", 
23457                     cn: [
23458                         inputblock
23459                     ]
23460                 }
23461             ];
23462             
23463             if (boxLabelCfg) {
23464                 cfg.cn[1].cn.push(boxLabelCfg);
23465             }
23466             
23467             if(this.labelWidth > 12){
23468                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23469             }
23470             
23471             if(this.labelWidth < 13 && this.labelmd == 0){
23472                 this.labelmd = this.labelWidth;
23473             }
23474             
23475             if(this.labellg > 0){
23476                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23477                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23478             }
23479             
23480             if(this.labelmd > 0){
23481                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23482                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23483             }
23484             
23485             if(this.labelsm > 0){
23486                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23487                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23488             }
23489             
23490             if(this.labelxs > 0){
23491                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23492                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23493             }
23494             
23495         } else if ( this.fieldLabel.length) {
23496 //                Roo.log(" label");
23497                 cfg.cn = [
23498                    
23499                     {
23500                         tag: this.boxLabel ? 'span' : 'label',
23501                         'for': id,
23502                         cls: 'control-label box-input-label',
23503                         //cls : 'input-group-addon',
23504                         html : this.fieldLabel
23505                     },
23506                     
23507                     inputblock
23508                     
23509                 ];
23510                 if (boxLabelCfg) {
23511                     cfg.cn.push(boxLabelCfg);
23512                 }
23513
23514         } else {
23515             
23516 //                Roo.log(" no label && no align");
23517                 cfg.cn = [  inputblock ] ;
23518                 if (boxLabelCfg) {
23519                     cfg.cn.push(boxLabelCfg);
23520                 }
23521
23522                 
23523         }
23524         
23525        
23526         
23527         if(this.inputType != 'radio'){
23528             cfg.cn.push(hidden);
23529         }
23530         
23531         return cfg;
23532         
23533     },
23534     
23535     /**
23536      * return the real input element.
23537      */
23538     inputEl: function ()
23539     {
23540         return this.el.select('input.roo-' + this.inputType,true).first();
23541     },
23542     hiddenEl: function ()
23543     {
23544         return this.el.select('input.roo-hidden-value',true).first();
23545     },
23546     
23547     labelEl: function()
23548     {
23549         return this.el.select('label.control-label',true).first();
23550     },
23551     /* depricated... */
23552     
23553     label: function()
23554     {
23555         return this.labelEl();
23556     },
23557     
23558     boxLabelEl: function()
23559     {
23560         return this.el.select('label.box-label',true).first();
23561     },
23562     
23563     initEvents : function()
23564     {
23565 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23566         
23567         this.inputEl().on('click', this.onClick,  this);
23568         
23569         if (this.boxLabel) { 
23570             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23571         }
23572         
23573         this.startValue = this.getValue();
23574         
23575         if(this.groupId){
23576             Roo.bootstrap.CheckBox.register(this);
23577         }
23578     },
23579     
23580     onClick : function(e)
23581     {   
23582         if(this.fireEvent('click', this, e) !== false){
23583             this.setChecked(!this.checked);
23584         }
23585         
23586     },
23587     
23588     setChecked : function(state,suppressEvent)
23589     {
23590         this.startValue = this.getValue();
23591
23592         if(this.inputType == 'radio'){
23593             
23594             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23595                 e.dom.checked = false;
23596             });
23597             
23598             this.inputEl().dom.checked = true;
23599             
23600             this.inputEl().dom.value = this.inputValue;
23601             
23602             if(suppressEvent !== true){
23603                 this.fireEvent('check', this, true);
23604             }
23605             
23606             this.validate();
23607             
23608             return;
23609         }
23610         
23611         this.checked = state;
23612         
23613         this.inputEl().dom.checked = state;
23614         
23615         
23616         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23617         
23618         if(suppressEvent !== true){
23619             this.fireEvent('check', this, state);
23620         }
23621         
23622         this.validate();
23623     },
23624     
23625     getValue : function()
23626     {
23627         if(this.inputType == 'radio'){
23628             return this.getGroupValue();
23629         }
23630         
23631         return this.hiddenEl().dom.value;
23632         
23633     },
23634     
23635     getGroupValue : function()
23636     {
23637         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23638             return '';
23639         }
23640         
23641         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23642     },
23643     
23644     setValue : function(v,suppressEvent)
23645     {
23646         if(this.inputType == 'radio'){
23647             this.setGroupValue(v, suppressEvent);
23648             return;
23649         }
23650         
23651         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23652         
23653         this.validate();
23654     },
23655     
23656     setGroupValue : function(v, suppressEvent)
23657     {
23658         this.startValue = this.getValue();
23659         
23660         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23661             e.dom.checked = false;
23662             
23663             if(e.dom.value == v){
23664                 e.dom.checked = true;
23665             }
23666         });
23667         
23668         if(suppressEvent !== true){
23669             this.fireEvent('check', this, true);
23670         }
23671
23672         this.validate();
23673         
23674         return;
23675     },
23676     
23677     validate : function()
23678     {
23679         if(this.getVisibilityEl().hasClass('hidden')){
23680             return true;
23681         }
23682         
23683         if(
23684                 this.disabled || 
23685                 (this.inputType == 'radio' && this.validateRadio()) ||
23686                 (this.inputType == 'checkbox' && this.validateCheckbox())
23687         ){
23688             this.markValid();
23689             return true;
23690         }
23691         
23692         this.markInvalid();
23693         return false;
23694     },
23695     
23696     validateRadio : function()
23697     {
23698         if(this.getVisibilityEl().hasClass('hidden')){
23699             return true;
23700         }
23701         
23702         if(this.allowBlank){
23703             return true;
23704         }
23705         
23706         var valid = false;
23707         
23708         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23709             if(!e.dom.checked){
23710                 return;
23711             }
23712             
23713             valid = true;
23714             
23715             return false;
23716         });
23717         
23718         return valid;
23719     },
23720     
23721     validateCheckbox : function()
23722     {
23723         if(!this.groupId){
23724             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23725             //return (this.getValue() == this.inputValue) ? true : false;
23726         }
23727         
23728         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23729         
23730         if(!group){
23731             return false;
23732         }
23733         
23734         var r = false;
23735         
23736         for(var i in group){
23737             if(group[i].el.isVisible(true)){
23738                 r = false;
23739                 break;
23740             }
23741             
23742             r = true;
23743         }
23744         
23745         for(var i in group){
23746             if(r){
23747                 break;
23748             }
23749             
23750             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23751         }
23752         
23753         return r;
23754     },
23755     
23756     /**
23757      * Mark this field as valid
23758      */
23759     markValid : function()
23760     {
23761         var _this = this;
23762         
23763         this.fireEvent('valid', this);
23764         
23765         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23766         
23767         if(this.groupId){
23768             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23769         }
23770         
23771         if(label){
23772             label.markValid();
23773         }
23774
23775         if(this.inputType == 'radio'){
23776             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23777                 var fg = e.findParent('.form-group', false, true);
23778                 if (Roo.bootstrap.version == 3) {
23779                     fg.removeClass([_this.invalidClass, _this.validClass]);
23780                     fg.addClass(_this.validClass);
23781                 } else {
23782                     fg.removeClass(['is-valid', 'is-invalid']);
23783                     fg.addClass('is-valid');
23784                 }
23785             });
23786             
23787             return;
23788         }
23789
23790         if(!this.groupId){
23791             var fg = this.el.findParent('.form-group', false, true);
23792             if (Roo.bootstrap.version == 3) {
23793                 fg.removeClass([this.invalidClass, this.validClass]);
23794                 fg.addClass(this.validClass);
23795             } else {
23796                 fg.removeClass(['is-valid', 'is-invalid']);
23797                 fg.addClass('is-valid');
23798             }
23799             return;
23800         }
23801         
23802         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23803         
23804         if(!group){
23805             return;
23806         }
23807         
23808         for(var i in group){
23809             var fg = group[i].el.findParent('.form-group', false, true);
23810             if (Roo.bootstrap.version == 3) {
23811                 fg.removeClass([this.invalidClass, this.validClass]);
23812                 fg.addClass(this.validClass);
23813             } else {
23814                 fg.removeClass(['is-valid', 'is-invalid']);
23815                 fg.addClass('is-valid');
23816             }
23817         }
23818     },
23819     
23820      /**
23821      * Mark this field as invalid
23822      * @param {String} msg The validation message
23823      */
23824     markInvalid : function(msg)
23825     {
23826         if(this.allowBlank){
23827             return;
23828         }
23829         
23830         var _this = this;
23831         
23832         this.fireEvent('invalid', this, msg);
23833         
23834         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23835         
23836         if(this.groupId){
23837             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23838         }
23839         
23840         if(label){
23841             label.markInvalid();
23842         }
23843             
23844         if(this.inputType == 'radio'){
23845             
23846             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23847                 var fg = e.findParent('.form-group', false, true);
23848                 if (Roo.bootstrap.version == 3) {
23849                     fg.removeClass([_this.invalidClass, _this.validClass]);
23850                     fg.addClass(_this.invalidClass);
23851                 } else {
23852                     fg.removeClass(['is-invalid', 'is-valid']);
23853                     fg.addClass('is-invalid');
23854                 }
23855             });
23856             
23857             return;
23858         }
23859         
23860         if(!this.groupId){
23861             var fg = this.el.findParent('.form-group', false, true);
23862             if (Roo.bootstrap.version == 3) {
23863                 fg.removeClass([_this.invalidClass, _this.validClass]);
23864                 fg.addClass(_this.invalidClass);
23865             } else {
23866                 fg.removeClass(['is-invalid', 'is-valid']);
23867                 fg.addClass('is-invalid');
23868             }
23869             return;
23870         }
23871         
23872         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23873         
23874         if(!group){
23875             return;
23876         }
23877         
23878         for(var i in group){
23879             var fg = group[i].el.findParent('.form-group', false, true);
23880             if (Roo.bootstrap.version == 3) {
23881                 fg.removeClass([_this.invalidClass, _this.validClass]);
23882                 fg.addClass(_this.invalidClass);
23883             } else {
23884                 fg.removeClass(['is-invalid', 'is-valid']);
23885                 fg.addClass('is-invalid');
23886             }
23887         }
23888         
23889     },
23890     
23891     clearInvalid : function()
23892     {
23893         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23894         
23895         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23896         
23897         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23898         
23899         if (label && label.iconEl) {
23900             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23901             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23902         }
23903     },
23904     
23905     disable : function()
23906     {
23907         if(this.inputType != 'radio'){
23908             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23909             return;
23910         }
23911         
23912         var _this = this;
23913         
23914         if(this.rendered){
23915             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23916                 _this.getActionEl().addClass(this.disabledClass);
23917                 e.dom.disabled = true;
23918             });
23919         }
23920         
23921         this.disabled = true;
23922         this.fireEvent("disable", this);
23923         return this;
23924     },
23925
23926     enable : function()
23927     {
23928         if(this.inputType != 'radio'){
23929             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23930             return;
23931         }
23932         
23933         var _this = this;
23934         
23935         if(this.rendered){
23936             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23937                 _this.getActionEl().removeClass(this.disabledClass);
23938                 e.dom.disabled = false;
23939             });
23940         }
23941         
23942         this.disabled = false;
23943         this.fireEvent("enable", this);
23944         return this;
23945     },
23946     
23947     setBoxLabel : function(v)
23948     {
23949         this.boxLabel = v;
23950         
23951         if(this.rendered){
23952             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23953         }
23954     }
23955
23956 });
23957
23958 Roo.apply(Roo.bootstrap.CheckBox, {
23959     
23960     groups: {},
23961     
23962      /**
23963     * register a CheckBox Group
23964     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23965     */
23966     register : function(checkbox)
23967     {
23968         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23969             this.groups[checkbox.groupId] = {};
23970         }
23971         
23972         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23973             return;
23974         }
23975         
23976         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23977         
23978     },
23979     /**
23980     * fetch a CheckBox Group based on the group ID
23981     * @param {string} the group ID
23982     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23983     */
23984     get: function(groupId) {
23985         if (typeof(this.groups[groupId]) == 'undefined') {
23986             return false;
23987         }
23988         
23989         return this.groups[groupId] ;
23990     }
23991     
23992     
23993 });
23994 /*
23995  * - LGPL
23996  *
23997  * RadioItem
23998  * 
23999  */
24000
24001 /**
24002  * @class Roo.bootstrap.Radio
24003  * @extends Roo.bootstrap.Component
24004  * Bootstrap Radio class
24005  * @cfg {String} boxLabel - the label associated
24006  * @cfg {String} value - the value of radio
24007  * 
24008  * @constructor
24009  * Create a new Radio
24010  * @param {Object} config The config object
24011  */
24012 Roo.bootstrap.Radio = function(config){
24013     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24014     
24015 };
24016
24017 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24018     
24019     boxLabel : '',
24020     
24021     value : '',
24022     
24023     getAutoCreate : function()
24024     {
24025         var cfg = {
24026             tag : 'div',
24027             cls : 'form-group radio',
24028             cn : [
24029                 {
24030                     tag : 'label',
24031                     cls : 'box-label',
24032                     html : this.boxLabel
24033                 }
24034             ]
24035         };
24036         
24037         return cfg;
24038     },
24039     
24040     initEvents : function() 
24041     {
24042         this.parent().register(this);
24043         
24044         this.el.on('click', this.onClick, this);
24045         
24046     },
24047     
24048     onClick : function(e)
24049     {
24050         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24051             this.setChecked(true);
24052         }
24053     },
24054     
24055     setChecked : function(state, suppressEvent)
24056     {
24057         this.parent().setValue(this.value, suppressEvent);
24058         
24059     },
24060     
24061     setBoxLabel : function(v)
24062     {
24063         this.boxLabel = v;
24064         
24065         if(this.rendered){
24066             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24067         }
24068     }
24069     
24070 });
24071  
24072
24073  /*
24074  * - LGPL
24075  *
24076  * Input
24077  * 
24078  */
24079
24080 /**
24081  * @class Roo.bootstrap.SecurePass
24082  * @extends Roo.bootstrap.Input
24083  * Bootstrap SecurePass class
24084  *
24085  * 
24086  * @constructor
24087  * Create a new SecurePass
24088  * @param {Object} config The config object
24089  */
24090  
24091 Roo.bootstrap.SecurePass = function (config) {
24092     // these go here, so the translation tool can replace them..
24093     this.errors = {
24094         PwdEmpty: "Please type a password, and then retype it to confirm.",
24095         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24096         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24097         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24098         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24099         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24100         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24101         TooWeak: "Your password is Too Weak."
24102     },
24103     this.meterLabel = "Password strength:";
24104     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24105     this.meterClass = [
24106         "roo-password-meter-tooweak", 
24107         "roo-password-meter-weak", 
24108         "roo-password-meter-medium", 
24109         "roo-password-meter-strong", 
24110         "roo-password-meter-grey"
24111     ];
24112     
24113     this.errors = {};
24114     
24115     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24116 }
24117
24118 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24119     /**
24120      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24121      * {
24122      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24123      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24124      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24125      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24126      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24127      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24128      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24129      * })
24130      */
24131     // private
24132     
24133     meterWidth: 300,
24134     errorMsg :'',    
24135     errors: false,
24136     imageRoot: '/',
24137     /**
24138      * @cfg {String/Object} Label for the strength meter (defaults to
24139      * 'Password strength:')
24140      */
24141     // private
24142     meterLabel: '',
24143     /**
24144      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24145      * ['Weak', 'Medium', 'Strong'])
24146      */
24147     // private    
24148     pwdStrengths: false,    
24149     // private
24150     strength: 0,
24151     // private
24152     _lastPwd: null,
24153     // private
24154     kCapitalLetter: 0,
24155     kSmallLetter: 1,
24156     kDigit: 2,
24157     kPunctuation: 3,
24158     
24159     insecure: false,
24160     // private
24161     initEvents: function ()
24162     {
24163         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24164
24165         if (this.el.is('input[type=password]') && Roo.isSafari) {
24166             this.el.on('keydown', this.SafariOnKeyDown, this);
24167         }
24168
24169         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24170     },
24171     // private
24172     onRender: function (ct, position)
24173     {
24174         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24175         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24176         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24177
24178         this.trigger.createChild({
24179                    cn: [
24180                     {
24181                     //id: 'PwdMeter',
24182                     tag: 'div',
24183                     cls: 'roo-password-meter-grey col-xs-12',
24184                     style: {
24185                         //width: 0,
24186                         //width: this.meterWidth + 'px'                                                
24187                         }
24188                     },
24189                     {                            
24190                          cls: 'roo-password-meter-text'                          
24191                     }
24192                 ]            
24193         });
24194
24195          
24196         if (this.hideTrigger) {
24197             this.trigger.setDisplayed(false);
24198         }
24199         this.setSize(this.width || '', this.height || '');
24200     },
24201     // private
24202     onDestroy: function ()
24203     {
24204         if (this.trigger) {
24205             this.trigger.removeAllListeners();
24206             this.trigger.remove();
24207         }
24208         if (this.wrap) {
24209             this.wrap.remove();
24210         }
24211         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24212     },
24213     // private
24214     checkStrength: function ()
24215     {
24216         var pwd = this.inputEl().getValue();
24217         if (pwd == this._lastPwd) {
24218             return;
24219         }
24220
24221         var strength;
24222         if (this.ClientSideStrongPassword(pwd)) {
24223             strength = 3;
24224         } else if (this.ClientSideMediumPassword(pwd)) {
24225             strength = 2;
24226         } else if (this.ClientSideWeakPassword(pwd)) {
24227             strength = 1;
24228         } else {
24229             strength = 0;
24230         }
24231         
24232         Roo.log('strength1: ' + strength);
24233         
24234         //var pm = this.trigger.child('div/div/div').dom;
24235         var pm = this.trigger.child('div/div');
24236         pm.removeClass(this.meterClass);
24237         pm.addClass(this.meterClass[strength]);
24238                 
24239         
24240         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24241                 
24242         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24243         
24244         this._lastPwd = pwd;
24245     },
24246     reset: function ()
24247     {
24248         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24249         
24250         this._lastPwd = '';
24251         
24252         var pm = this.trigger.child('div/div');
24253         pm.removeClass(this.meterClass);
24254         pm.addClass('roo-password-meter-grey');        
24255         
24256         
24257         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24258         
24259         pt.innerHTML = '';
24260         this.inputEl().dom.type='password';
24261     },
24262     // private
24263     validateValue: function (value)
24264     {
24265         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24266             return false;
24267         }
24268         if (value.length == 0) {
24269             if (this.allowBlank) {
24270                 this.clearInvalid();
24271                 return true;
24272             }
24273
24274             this.markInvalid(this.errors.PwdEmpty);
24275             this.errorMsg = this.errors.PwdEmpty;
24276             return false;
24277         }
24278         
24279         if(this.insecure){
24280             return true;
24281         }
24282         
24283         if (!value.match(/[\x21-\x7e]+/)) {
24284             this.markInvalid(this.errors.PwdBadChar);
24285             this.errorMsg = this.errors.PwdBadChar;
24286             return false;
24287         }
24288         if (value.length < 6) {
24289             this.markInvalid(this.errors.PwdShort);
24290             this.errorMsg = this.errors.PwdShort;
24291             return false;
24292         }
24293         if (value.length > 16) {
24294             this.markInvalid(this.errors.PwdLong);
24295             this.errorMsg = this.errors.PwdLong;
24296             return false;
24297         }
24298         var strength;
24299         if (this.ClientSideStrongPassword(value)) {
24300             strength = 3;
24301         } else if (this.ClientSideMediumPassword(value)) {
24302             strength = 2;
24303         } else if (this.ClientSideWeakPassword(value)) {
24304             strength = 1;
24305         } else {
24306             strength = 0;
24307         }
24308
24309         
24310         if (strength < 2) {
24311             //this.markInvalid(this.errors.TooWeak);
24312             this.errorMsg = this.errors.TooWeak;
24313             //return false;
24314         }
24315         
24316         
24317         console.log('strength2: ' + strength);
24318         
24319         //var pm = this.trigger.child('div/div/div').dom;
24320         
24321         var pm = this.trigger.child('div/div');
24322         pm.removeClass(this.meterClass);
24323         pm.addClass(this.meterClass[strength]);
24324                 
24325         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24326                 
24327         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24328         
24329         this.errorMsg = ''; 
24330         return true;
24331     },
24332     // private
24333     CharacterSetChecks: function (type)
24334     {
24335         this.type = type;
24336         this.fResult = false;
24337     },
24338     // private
24339     isctype: function (character, type)
24340     {
24341         switch (type) {  
24342             case this.kCapitalLetter:
24343                 if (character >= 'A' && character <= 'Z') {
24344                     return true;
24345                 }
24346                 break;
24347             
24348             case this.kSmallLetter:
24349                 if (character >= 'a' && character <= 'z') {
24350                     return true;
24351                 }
24352                 break;
24353             
24354             case this.kDigit:
24355                 if (character >= '0' && character <= '9') {
24356                     return true;
24357                 }
24358                 break;
24359             
24360             case this.kPunctuation:
24361                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24362                     return true;
24363                 }
24364                 break;
24365             
24366             default:
24367                 return false;
24368         }
24369
24370     },
24371     // private
24372     IsLongEnough: function (pwd, size)
24373     {
24374         return !(pwd == null || isNaN(size) || pwd.length < size);
24375     },
24376     // private
24377     SpansEnoughCharacterSets: function (word, nb)
24378     {
24379         if (!this.IsLongEnough(word, nb))
24380         {
24381             return false;
24382         }
24383
24384         var characterSetChecks = new Array(
24385             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24386             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24387         );
24388         
24389         for (var index = 0; index < word.length; ++index) {
24390             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24391                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24392                     characterSetChecks[nCharSet].fResult = true;
24393                     break;
24394                 }
24395             }
24396         }
24397
24398         var nCharSets = 0;
24399         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24400             if (characterSetChecks[nCharSet].fResult) {
24401                 ++nCharSets;
24402             }
24403         }
24404
24405         if (nCharSets < nb) {
24406             return false;
24407         }
24408         return true;
24409     },
24410     // private
24411     ClientSideStrongPassword: function (pwd)
24412     {
24413         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24414     },
24415     // private
24416     ClientSideMediumPassword: function (pwd)
24417     {
24418         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24419     },
24420     // private
24421     ClientSideWeakPassword: function (pwd)
24422     {
24423         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24424     }
24425           
24426 })//<script type="text/javascript">
24427
24428 /*
24429  * Based  Ext JS Library 1.1.1
24430  * Copyright(c) 2006-2007, Ext JS, LLC.
24431  * LGPL
24432  *
24433  */
24434  
24435 /**
24436  * @class Roo.HtmlEditorCore
24437  * @extends Roo.Component
24438  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24439  *
24440  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24441  */
24442
24443 Roo.HtmlEditorCore = function(config){
24444     
24445     
24446     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24447     
24448     
24449     this.addEvents({
24450         /**
24451          * @event initialize
24452          * Fires when the editor is fully initialized (including the iframe)
24453          * @param {Roo.HtmlEditorCore} this
24454          */
24455         initialize: true,
24456         /**
24457          * @event activate
24458          * Fires when the editor is first receives the focus. Any insertion must wait
24459          * until after this event.
24460          * @param {Roo.HtmlEditorCore} this
24461          */
24462         activate: true,
24463          /**
24464          * @event beforesync
24465          * Fires before the textarea is updated with content from the editor iframe. Return false
24466          * to cancel the sync.
24467          * @param {Roo.HtmlEditorCore} this
24468          * @param {String} html
24469          */
24470         beforesync: true,
24471          /**
24472          * @event beforepush
24473          * Fires before the iframe editor is updated with content from the textarea. Return false
24474          * to cancel the push.
24475          * @param {Roo.HtmlEditorCore} this
24476          * @param {String} html
24477          */
24478         beforepush: true,
24479          /**
24480          * @event sync
24481          * Fires when the textarea is updated with content from the editor iframe.
24482          * @param {Roo.HtmlEditorCore} this
24483          * @param {String} html
24484          */
24485         sync: true,
24486          /**
24487          * @event push
24488          * Fires when the iframe editor is updated with content from the textarea.
24489          * @param {Roo.HtmlEditorCore} this
24490          * @param {String} html
24491          */
24492         push: true,
24493         
24494         /**
24495          * @event editorevent
24496          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24497          * @param {Roo.HtmlEditorCore} this
24498          */
24499         editorevent: true
24500         
24501     });
24502     
24503     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24504     
24505     // defaults : white / black...
24506     this.applyBlacklists();
24507     
24508     
24509     
24510 };
24511
24512
24513 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24514
24515
24516      /**
24517      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24518      */
24519     
24520     owner : false,
24521     
24522      /**
24523      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24524      *                        Roo.resizable.
24525      */
24526     resizable : false,
24527      /**
24528      * @cfg {Number} height (in pixels)
24529      */   
24530     height: 300,
24531    /**
24532      * @cfg {Number} width (in pixels)
24533      */   
24534     width: 500,
24535     
24536     /**
24537      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24538      * 
24539      */
24540     stylesheets: false,
24541     
24542     // id of frame..
24543     frameId: false,
24544     
24545     // private properties
24546     validationEvent : false,
24547     deferHeight: true,
24548     initialized : false,
24549     activated : false,
24550     sourceEditMode : false,
24551     onFocus : Roo.emptyFn,
24552     iframePad:3,
24553     hideMode:'offsets',
24554     
24555     clearUp: true,
24556     
24557     // blacklist + whitelisted elements..
24558     black: false,
24559     white: false,
24560      
24561     bodyCls : '',
24562
24563     /**
24564      * Protected method that will not generally be called directly. It
24565      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24566      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24567      */
24568     getDocMarkup : function(){
24569         // body styles..
24570         var st = '';
24571         
24572         // inherit styels from page...?? 
24573         if (this.stylesheets === false) {
24574             
24575             Roo.get(document.head).select('style').each(function(node) {
24576                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24577             });
24578             
24579             Roo.get(document.head).select('link').each(function(node) { 
24580                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24581             });
24582             
24583         } else if (!this.stylesheets.length) {
24584                 // simple..
24585                 st = '<style type="text/css">' +
24586                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24587                    '</style>';
24588         } else {
24589             for (var i in this.stylesheets) { 
24590                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24591             }
24592             
24593         }
24594         
24595         st +=  '<style type="text/css">' +
24596             'IMG { cursor: pointer } ' +
24597         '</style>';
24598
24599         var cls = 'roo-htmleditor-body';
24600         
24601         if(this.bodyCls.length){
24602             cls += ' ' + this.bodyCls;
24603         }
24604         
24605         return '<html><head>' + st  +
24606             //<style type="text/css">' +
24607             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24608             //'</style>' +
24609             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24610     },
24611
24612     // private
24613     onRender : function(ct, position)
24614     {
24615         var _t = this;
24616         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24617         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24618         
24619         
24620         this.el.dom.style.border = '0 none';
24621         this.el.dom.setAttribute('tabIndex', -1);
24622         this.el.addClass('x-hidden hide');
24623         
24624         
24625         
24626         if(Roo.isIE){ // fix IE 1px bogus margin
24627             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24628         }
24629        
24630         
24631         this.frameId = Roo.id();
24632         
24633          
24634         
24635         var iframe = this.owner.wrap.createChild({
24636             tag: 'iframe',
24637             cls: 'form-control', // bootstrap..
24638             id: this.frameId,
24639             name: this.frameId,
24640             frameBorder : 'no',
24641             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24642         }, this.el
24643         );
24644         
24645         
24646         this.iframe = iframe.dom;
24647
24648          this.assignDocWin();
24649         
24650         this.doc.designMode = 'on';
24651        
24652         this.doc.open();
24653         this.doc.write(this.getDocMarkup());
24654         this.doc.close();
24655
24656         
24657         var task = { // must defer to wait for browser to be ready
24658             run : function(){
24659                 //console.log("run task?" + this.doc.readyState);
24660                 this.assignDocWin();
24661                 if(this.doc.body || this.doc.readyState == 'complete'){
24662                     try {
24663                         this.doc.designMode="on";
24664                     } catch (e) {
24665                         return;
24666                     }
24667                     Roo.TaskMgr.stop(task);
24668                     this.initEditor.defer(10, this);
24669                 }
24670             },
24671             interval : 10,
24672             duration: 10000,
24673             scope: this
24674         };
24675         Roo.TaskMgr.start(task);
24676
24677     },
24678
24679     // private
24680     onResize : function(w, h)
24681     {
24682          Roo.log('resize: ' +w + ',' + h );
24683         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24684         if(!this.iframe){
24685             return;
24686         }
24687         if(typeof w == 'number'){
24688             
24689             this.iframe.style.width = w + 'px';
24690         }
24691         if(typeof h == 'number'){
24692             
24693             this.iframe.style.height = h + 'px';
24694             if(this.doc){
24695                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24696             }
24697         }
24698         
24699     },
24700
24701     /**
24702      * Toggles the editor between standard and source edit mode.
24703      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24704      */
24705     toggleSourceEdit : function(sourceEditMode){
24706         
24707         this.sourceEditMode = sourceEditMode === true;
24708         
24709         if(this.sourceEditMode){
24710  
24711             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24712             
24713         }else{
24714             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24715             //this.iframe.className = '';
24716             this.deferFocus();
24717         }
24718         //this.setSize(this.owner.wrap.getSize());
24719         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24720     },
24721
24722     
24723   
24724
24725     /**
24726      * Protected method that will not generally be called directly. If you need/want
24727      * custom HTML cleanup, this is the method you should override.
24728      * @param {String} html The HTML to be cleaned
24729      * return {String} The cleaned HTML
24730      */
24731     cleanHtml : function(html){
24732         html = String(html);
24733         if(html.length > 5){
24734             if(Roo.isSafari){ // strip safari nonsense
24735                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24736             }
24737         }
24738         if(html == '&nbsp;'){
24739             html = '';
24740         }
24741         return html;
24742     },
24743
24744     /**
24745      * HTML Editor -> Textarea
24746      * Protected method that will not generally be called directly. Syncs the contents
24747      * of the editor iframe with the textarea.
24748      */
24749     syncValue : function(){
24750         if(this.initialized){
24751             var bd = (this.doc.body || this.doc.documentElement);
24752             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24753             var html = bd.innerHTML;
24754             if(Roo.isSafari){
24755                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24756                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24757                 if(m && m[1]){
24758                     html = '<div style="'+m[0]+'">' + html + '</div>';
24759                 }
24760             }
24761             html = this.cleanHtml(html);
24762             // fix up the special chars.. normaly like back quotes in word...
24763             // however we do not want to do this with chinese..
24764             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24765                 
24766                 var cc = match.charCodeAt();
24767
24768                 // Get the character value, handling surrogate pairs
24769                 if (match.length == 2) {
24770                     // It's a surrogate pair, calculate the Unicode code point
24771                     var high = match.charCodeAt(0) - 0xD800;
24772                     var low  = match.charCodeAt(1) - 0xDC00;
24773                     cc = (high * 0x400) + low + 0x10000;
24774                 }  else if (
24775                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24776                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24777                     (cc >= 0xf900 && cc < 0xfb00 )
24778                 ) {
24779                         return match;
24780                 }  
24781          
24782                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24783                 return "&#" + cc + ";";
24784                 
24785                 
24786             });
24787             
24788             
24789              
24790             if(this.owner.fireEvent('beforesync', this, html) !== false){
24791                 this.el.dom.value = html;
24792                 this.owner.fireEvent('sync', this, html);
24793             }
24794         }
24795     },
24796
24797     /**
24798      * Protected method that will not generally be called directly. Pushes the value of the textarea
24799      * into the iframe editor.
24800      */
24801     pushValue : function(){
24802         if(this.initialized){
24803             var v = this.el.dom.value.trim();
24804             
24805 //            if(v.length < 1){
24806 //                v = '&#160;';
24807 //            }
24808             
24809             if(this.owner.fireEvent('beforepush', this, v) !== false){
24810                 var d = (this.doc.body || this.doc.documentElement);
24811                 d.innerHTML = v;
24812                 this.cleanUpPaste();
24813                 this.el.dom.value = d.innerHTML;
24814                 this.owner.fireEvent('push', this, v);
24815             }
24816         }
24817     },
24818
24819     // private
24820     deferFocus : function(){
24821         this.focus.defer(10, this);
24822     },
24823
24824     // doc'ed in Field
24825     focus : function(){
24826         if(this.win && !this.sourceEditMode){
24827             this.win.focus();
24828         }else{
24829             this.el.focus();
24830         }
24831     },
24832     
24833     assignDocWin: function()
24834     {
24835         var iframe = this.iframe;
24836         
24837          if(Roo.isIE){
24838             this.doc = iframe.contentWindow.document;
24839             this.win = iframe.contentWindow;
24840         } else {
24841 //            if (!Roo.get(this.frameId)) {
24842 //                return;
24843 //            }
24844 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24845 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24846             
24847             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24848                 return;
24849             }
24850             
24851             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24852             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24853         }
24854     },
24855     
24856     // private
24857     initEditor : function(){
24858         //console.log("INIT EDITOR");
24859         this.assignDocWin();
24860         
24861         
24862         
24863         this.doc.designMode="on";
24864         this.doc.open();
24865         this.doc.write(this.getDocMarkup());
24866         this.doc.close();
24867         
24868         var dbody = (this.doc.body || this.doc.documentElement);
24869         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24870         // this copies styles from the containing element into thsi one..
24871         // not sure why we need all of this..
24872         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24873         
24874         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24875         //ss['background-attachment'] = 'fixed'; // w3c
24876         dbody.bgProperties = 'fixed'; // ie
24877         //Roo.DomHelper.applyStyles(dbody, ss);
24878         Roo.EventManager.on(this.doc, {
24879             //'mousedown': this.onEditorEvent,
24880             'mouseup': this.onEditorEvent,
24881             'dblclick': this.onEditorEvent,
24882             'click': this.onEditorEvent,
24883             'keyup': this.onEditorEvent,
24884             buffer:100,
24885             scope: this
24886         });
24887         if(Roo.isGecko){
24888             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24889         }
24890         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24891             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24892         }
24893         this.initialized = true;
24894
24895         this.owner.fireEvent('initialize', this);
24896         this.pushValue();
24897     },
24898
24899     // private
24900     onDestroy : function(){
24901         
24902         
24903         
24904         if(this.rendered){
24905             
24906             //for (var i =0; i < this.toolbars.length;i++) {
24907             //    // fixme - ask toolbars for heights?
24908             //    this.toolbars[i].onDestroy();
24909            // }
24910             
24911             //this.wrap.dom.innerHTML = '';
24912             //this.wrap.remove();
24913         }
24914     },
24915
24916     // private
24917     onFirstFocus : function(){
24918         
24919         this.assignDocWin();
24920         
24921         
24922         this.activated = true;
24923          
24924     
24925         if(Roo.isGecko){ // prevent silly gecko errors
24926             this.win.focus();
24927             var s = this.win.getSelection();
24928             if(!s.focusNode || s.focusNode.nodeType != 3){
24929                 var r = s.getRangeAt(0);
24930                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24931                 r.collapse(true);
24932                 this.deferFocus();
24933             }
24934             try{
24935                 this.execCmd('useCSS', true);
24936                 this.execCmd('styleWithCSS', false);
24937             }catch(e){}
24938         }
24939         this.owner.fireEvent('activate', this);
24940     },
24941
24942     // private
24943     adjustFont: function(btn){
24944         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24945         //if(Roo.isSafari){ // safari
24946         //    adjust *= 2;
24947        // }
24948         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24949         if(Roo.isSafari){ // safari
24950             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24951             v =  (v < 10) ? 10 : v;
24952             v =  (v > 48) ? 48 : v;
24953             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24954             
24955         }
24956         
24957         
24958         v = Math.max(1, v+adjust);
24959         
24960         this.execCmd('FontSize', v  );
24961     },
24962
24963     onEditorEvent : function(e)
24964     {
24965         this.owner.fireEvent('editorevent', this, e);
24966       //  this.updateToolbar();
24967         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24968     },
24969
24970     insertTag : function(tg)
24971     {
24972         // could be a bit smarter... -> wrap the current selected tRoo..
24973         if (tg.toLowerCase() == 'span' ||
24974             tg.toLowerCase() == 'code' ||
24975             tg.toLowerCase() == 'sup' ||
24976             tg.toLowerCase() == 'sub' 
24977             ) {
24978             
24979             range = this.createRange(this.getSelection());
24980             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24981             wrappingNode.appendChild(range.extractContents());
24982             range.insertNode(wrappingNode);
24983
24984             return;
24985             
24986             
24987             
24988         }
24989         this.execCmd("formatblock",   tg);
24990         
24991     },
24992     
24993     insertText : function(txt)
24994     {
24995         
24996         
24997         var range = this.createRange();
24998         range.deleteContents();
24999                //alert(Sender.getAttribute('label'));
25000                
25001         range.insertNode(this.doc.createTextNode(txt));
25002     } ,
25003     
25004      
25005
25006     /**
25007      * Executes a Midas editor command on the editor document and performs necessary focus and
25008      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25009      * @param {String} cmd The Midas command
25010      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25011      */
25012     relayCmd : function(cmd, value){
25013         this.win.focus();
25014         this.execCmd(cmd, value);
25015         this.owner.fireEvent('editorevent', this);
25016         //this.updateToolbar();
25017         this.owner.deferFocus();
25018     },
25019
25020     /**
25021      * Executes a Midas editor command directly on the editor document.
25022      * For visual commands, you should use {@link #relayCmd} instead.
25023      * <b>This should only be called after the editor is initialized.</b>
25024      * @param {String} cmd The Midas command
25025      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25026      */
25027     execCmd : function(cmd, value){
25028         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25029         this.syncValue();
25030     },
25031  
25032  
25033    
25034     /**
25035      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25036      * to insert tRoo.
25037      * @param {String} text | dom node.. 
25038      */
25039     insertAtCursor : function(text)
25040     {
25041         
25042         if(!this.activated){
25043             return;
25044         }
25045         /*
25046         if(Roo.isIE){
25047             this.win.focus();
25048             var r = this.doc.selection.createRange();
25049             if(r){
25050                 r.collapse(true);
25051                 r.pasteHTML(text);
25052                 this.syncValue();
25053                 this.deferFocus();
25054             
25055             }
25056             return;
25057         }
25058         */
25059         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25060             this.win.focus();
25061             
25062             
25063             // from jquery ui (MIT licenced)
25064             var range, node;
25065             var win = this.win;
25066             
25067             if (win.getSelection && win.getSelection().getRangeAt) {
25068                 range = win.getSelection().getRangeAt(0);
25069                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25070                 range.insertNode(node);
25071             } else if (win.document.selection && win.document.selection.createRange) {
25072                 // no firefox support
25073                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25074                 win.document.selection.createRange().pasteHTML(txt);
25075             } else {
25076                 // no firefox support
25077                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25078                 this.execCmd('InsertHTML', txt);
25079             } 
25080             
25081             this.syncValue();
25082             
25083             this.deferFocus();
25084         }
25085     },
25086  // private
25087     mozKeyPress : function(e){
25088         if(e.ctrlKey){
25089             var c = e.getCharCode(), cmd;
25090           
25091             if(c > 0){
25092                 c = String.fromCharCode(c).toLowerCase();
25093                 switch(c){
25094                     case 'b':
25095                         cmd = 'bold';
25096                         break;
25097                     case 'i':
25098                         cmd = 'italic';
25099                         break;
25100                     
25101                     case 'u':
25102                         cmd = 'underline';
25103                         break;
25104                     
25105                     case 'v':
25106                         this.cleanUpPaste.defer(100, this);
25107                         return;
25108                         
25109                 }
25110                 if(cmd){
25111                     this.win.focus();
25112                     this.execCmd(cmd);
25113                     this.deferFocus();
25114                     e.preventDefault();
25115                 }
25116                 
25117             }
25118         }
25119     },
25120
25121     // private
25122     fixKeys : function(){ // load time branching for fastest keydown performance
25123         if(Roo.isIE){
25124             return function(e){
25125                 var k = e.getKey(), r;
25126                 if(k == e.TAB){
25127                     e.stopEvent();
25128                     r = this.doc.selection.createRange();
25129                     if(r){
25130                         r.collapse(true);
25131                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25132                         this.deferFocus();
25133                     }
25134                     return;
25135                 }
25136                 
25137                 if(k == e.ENTER){
25138                     r = this.doc.selection.createRange();
25139                     if(r){
25140                         var target = r.parentElement();
25141                         if(!target || target.tagName.toLowerCase() != 'li'){
25142                             e.stopEvent();
25143                             r.pasteHTML('<br />');
25144                             r.collapse(false);
25145                             r.select();
25146                         }
25147                     }
25148                 }
25149                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25150                     this.cleanUpPaste.defer(100, this);
25151                     return;
25152                 }
25153                 
25154                 
25155             };
25156         }else if(Roo.isOpera){
25157             return function(e){
25158                 var k = e.getKey();
25159                 if(k == e.TAB){
25160                     e.stopEvent();
25161                     this.win.focus();
25162                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25163                     this.deferFocus();
25164                 }
25165                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25166                     this.cleanUpPaste.defer(100, this);
25167                     return;
25168                 }
25169                 
25170             };
25171         }else if(Roo.isSafari){
25172             return function(e){
25173                 var k = e.getKey();
25174                 
25175                 if(k == e.TAB){
25176                     e.stopEvent();
25177                     this.execCmd('InsertText','\t');
25178                     this.deferFocus();
25179                     return;
25180                 }
25181                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25182                     this.cleanUpPaste.defer(100, this);
25183                     return;
25184                 }
25185                 
25186              };
25187         }
25188     }(),
25189     
25190     getAllAncestors: function()
25191     {
25192         var p = this.getSelectedNode();
25193         var a = [];
25194         if (!p) {
25195             a.push(p); // push blank onto stack..
25196             p = this.getParentElement();
25197         }
25198         
25199         
25200         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25201             a.push(p);
25202             p = p.parentNode;
25203         }
25204         a.push(this.doc.body);
25205         return a;
25206     },
25207     lastSel : false,
25208     lastSelNode : false,
25209     
25210     
25211     getSelection : function() 
25212     {
25213         this.assignDocWin();
25214         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25215     },
25216     
25217     getSelectedNode: function() 
25218     {
25219         // this may only work on Gecko!!!
25220         
25221         // should we cache this!!!!
25222         
25223         
25224         
25225          
25226         var range = this.createRange(this.getSelection()).cloneRange();
25227         
25228         if (Roo.isIE) {
25229             var parent = range.parentElement();
25230             while (true) {
25231                 var testRange = range.duplicate();
25232                 testRange.moveToElementText(parent);
25233                 if (testRange.inRange(range)) {
25234                     break;
25235                 }
25236                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25237                     break;
25238                 }
25239                 parent = parent.parentElement;
25240             }
25241             return parent;
25242         }
25243         
25244         // is ancestor a text element.
25245         var ac =  range.commonAncestorContainer;
25246         if (ac.nodeType == 3) {
25247             ac = ac.parentNode;
25248         }
25249         
25250         var ar = ac.childNodes;
25251          
25252         var nodes = [];
25253         var other_nodes = [];
25254         var has_other_nodes = false;
25255         for (var i=0;i<ar.length;i++) {
25256             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25257                 continue;
25258             }
25259             // fullly contained node.
25260             
25261             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25262                 nodes.push(ar[i]);
25263                 continue;
25264             }
25265             
25266             // probably selected..
25267             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25268                 other_nodes.push(ar[i]);
25269                 continue;
25270             }
25271             // outer..
25272             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25273                 continue;
25274             }
25275             
25276             
25277             has_other_nodes = true;
25278         }
25279         if (!nodes.length && other_nodes.length) {
25280             nodes= other_nodes;
25281         }
25282         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25283             return false;
25284         }
25285         
25286         return nodes[0];
25287     },
25288     createRange: function(sel)
25289     {
25290         // this has strange effects when using with 
25291         // top toolbar - not sure if it's a great idea.
25292         //this.editor.contentWindow.focus();
25293         if (typeof sel != "undefined") {
25294             try {
25295                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25296             } catch(e) {
25297                 return this.doc.createRange();
25298             }
25299         } else {
25300             return this.doc.createRange();
25301         }
25302     },
25303     getParentElement: function()
25304     {
25305         
25306         this.assignDocWin();
25307         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25308         
25309         var range = this.createRange(sel);
25310          
25311         try {
25312             var p = range.commonAncestorContainer;
25313             while (p.nodeType == 3) { // text node
25314                 p = p.parentNode;
25315             }
25316             return p;
25317         } catch (e) {
25318             return null;
25319         }
25320     
25321     },
25322     /***
25323      *
25324      * Range intersection.. the hard stuff...
25325      *  '-1' = before
25326      *  '0' = hits..
25327      *  '1' = after.
25328      *         [ -- selected range --- ]
25329      *   [fail]                        [fail]
25330      *
25331      *    basically..
25332      *      if end is before start or  hits it. fail.
25333      *      if start is after end or hits it fail.
25334      *
25335      *   if either hits (but other is outside. - then it's not 
25336      *   
25337      *    
25338      **/
25339     
25340     
25341     // @see http://www.thismuchiknow.co.uk/?p=64.
25342     rangeIntersectsNode : function(range, node)
25343     {
25344         var nodeRange = node.ownerDocument.createRange();
25345         try {
25346             nodeRange.selectNode(node);
25347         } catch (e) {
25348             nodeRange.selectNodeContents(node);
25349         }
25350     
25351         var rangeStartRange = range.cloneRange();
25352         rangeStartRange.collapse(true);
25353     
25354         var rangeEndRange = range.cloneRange();
25355         rangeEndRange.collapse(false);
25356     
25357         var nodeStartRange = nodeRange.cloneRange();
25358         nodeStartRange.collapse(true);
25359     
25360         var nodeEndRange = nodeRange.cloneRange();
25361         nodeEndRange.collapse(false);
25362     
25363         return rangeStartRange.compareBoundaryPoints(
25364                  Range.START_TO_START, nodeEndRange) == -1 &&
25365                rangeEndRange.compareBoundaryPoints(
25366                  Range.START_TO_START, nodeStartRange) == 1;
25367         
25368          
25369     },
25370     rangeCompareNode : function(range, node)
25371     {
25372         var nodeRange = node.ownerDocument.createRange();
25373         try {
25374             nodeRange.selectNode(node);
25375         } catch (e) {
25376             nodeRange.selectNodeContents(node);
25377         }
25378         
25379         
25380         range.collapse(true);
25381     
25382         nodeRange.collapse(true);
25383      
25384         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25385         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25386          
25387         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25388         
25389         var nodeIsBefore   =  ss == 1;
25390         var nodeIsAfter    = ee == -1;
25391         
25392         if (nodeIsBefore && nodeIsAfter) {
25393             return 0; // outer
25394         }
25395         if (!nodeIsBefore && nodeIsAfter) {
25396             return 1; //right trailed.
25397         }
25398         
25399         if (nodeIsBefore && !nodeIsAfter) {
25400             return 2;  // left trailed.
25401         }
25402         // fully contined.
25403         return 3;
25404     },
25405
25406     // private? - in a new class?
25407     cleanUpPaste :  function()
25408     {
25409         // cleans up the whole document..
25410         Roo.log('cleanuppaste');
25411         
25412         this.cleanUpChildren(this.doc.body);
25413         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25414         if (clean != this.doc.body.innerHTML) {
25415             this.doc.body.innerHTML = clean;
25416         }
25417         
25418     },
25419     
25420     cleanWordChars : function(input) {// change the chars to hex code
25421         var he = Roo.HtmlEditorCore;
25422         
25423         var output = input;
25424         Roo.each(he.swapCodes, function(sw) { 
25425             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25426             
25427             output = output.replace(swapper, sw[1]);
25428         });
25429         
25430         return output;
25431     },
25432     
25433     
25434     cleanUpChildren : function (n)
25435     {
25436         if (!n.childNodes.length) {
25437             return;
25438         }
25439         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25440            this.cleanUpChild(n.childNodes[i]);
25441         }
25442     },
25443     
25444     
25445         
25446     
25447     cleanUpChild : function (node)
25448     {
25449         var ed = this;
25450         //console.log(node);
25451         if (node.nodeName == "#text") {
25452             // clean up silly Windows -- stuff?
25453             return; 
25454         }
25455         if (node.nodeName == "#comment") {
25456             node.parentNode.removeChild(node);
25457             // clean up silly Windows -- stuff?
25458             return; 
25459         }
25460         var lcname = node.tagName.toLowerCase();
25461         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25462         // whitelist of tags..
25463         
25464         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25465             // remove node.
25466             node.parentNode.removeChild(node);
25467             return;
25468             
25469         }
25470         
25471         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25472         
25473         // spans with no attributes - just remove them..
25474         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25475             remove_keep_children = true;
25476         }
25477         
25478         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25479         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25480         
25481         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25482         //    remove_keep_children = true;
25483         //}
25484         
25485         if (remove_keep_children) {
25486             this.cleanUpChildren(node);
25487             // inserts everything just before this node...
25488             while (node.childNodes.length) {
25489                 var cn = node.childNodes[0];
25490                 node.removeChild(cn);
25491                 node.parentNode.insertBefore(cn, node);
25492             }
25493             node.parentNode.removeChild(node);
25494             return;
25495         }
25496         
25497         if (!node.attributes || !node.attributes.length) {
25498             
25499           
25500             
25501             
25502             this.cleanUpChildren(node);
25503             return;
25504         }
25505         
25506         function cleanAttr(n,v)
25507         {
25508             
25509             if (v.match(/^\./) || v.match(/^\//)) {
25510                 return;
25511             }
25512             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25513                 return;
25514             }
25515             if (v.match(/^#/)) {
25516                 return;
25517             }
25518             if (v.match(/^\{/)) { // allow template editing.
25519                 return;
25520             }
25521 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25522             node.removeAttribute(n);
25523             
25524         }
25525         
25526         var cwhite = this.cwhite;
25527         var cblack = this.cblack;
25528             
25529         function cleanStyle(n,v)
25530         {
25531             if (v.match(/expression/)) { //XSS?? should we even bother..
25532                 node.removeAttribute(n);
25533                 return;
25534             }
25535             
25536             var parts = v.split(/;/);
25537             var clean = [];
25538             
25539             Roo.each(parts, function(p) {
25540                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25541                 if (!p.length) {
25542                     return true;
25543                 }
25544                 var l = p.split(':').shift().replace(/\s+/g,'');
25545                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25546                 
25547                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25548 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25549                     //node.removeAttribute(n);
25550                     return true;
25551                 }
25552                 //Roo.log()
25553                 // only allow 'c whitelisted system attributes'
25554                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25555 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25556                     //node.removeAttribute(n);
25557                     return true;
25558                 }
25559                 
25560                 
25561                  
25562                 
25563                 clean.push(p);
25564                 return true;
25565             });
25566             if (clean.length) { 
25567                 node.setAttribute(n, clean.join(';'));
25568             } else {
25569                 node.removeAttribute(n);
25570             }
25571             
25572         }
25573         
25574         
25575         for (var i = node.attributes.length-1; i > -1 ; i--) {
25576             var a = node.attributes[i];
25577             //console.log(a);
25578             
25579             if (a.name.toLowerCase().substr(0,2)=='on')  {
25580                 node.removeAttribute(a.name);
25581                 continue;
25582             }
25583             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25584                 node.removeAttribute(a.name);
25585                 continue;
25586             }
25587             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25588                 cleanAttr(a.name,a.value); // fixme..
25589                 continue;
25590             }
25591             if (a.name == 'style') {
25592                 cleanStyle(a.name,a.value);
25593                 continue;
25594             }
25595             /// clean up MS crap..
25596             // tecnically this should be a list of valid class'es..
25597             
25598             
25599             if (a.name == 'class') {
25600                 if (a.value.match(/^Mso/)) {
25601                     node.removeAttribute('class');
25602                 }
25603                 
25604                 if (a.value.match(/^body$/)) {
25605                     node.removeAttribute('class');
25606                 }
25607                 continue;
25608             }
25609             
25610             // style cleanup!?
25611             // class cleanup?
25612             
25613         }
25614         
25615         
25616         this.cleanUpChildren(node);
25617         
25618         
25619     },
25620     
25621     /**
25622      * Clean up MS wordisms...
25623      */
25624     cleanWord : function(node)
25625     {
25626         if (!node) {
25627             this.cleanWord(this.doc.body);
25628             return;
25629         }
25630         
25631         if(
25632                 node.nodeName == 'SPAN' &&
25633                 !node.hasAttributes() &&
25634                 node.childNodes.length == 1 &&
25635                 node.firstChild.nodeName == "#text"  
25636         ) {
25637             var textNode = node.firstChild;
25638             node.removeChild(textNode);
25639             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25640                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25641             }
25642             node.parentNode.insertBefore(textNode, node);
25643             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25644                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25645             }
25646             node.parentNode.removeChild(node);
25647         }
25648         
25649         if (node.nodeName == "#text") {
25650             // clean up silly Windows -- stuff?
25651             return; 
25652         }
25653         if (node.nodeName == "#comment") {
25654             node.parentNode.removeChild(node);
25655             // clean up silly Windows -- stuff?
25656             return; 
25657         }
25658         
25659         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25660             node.parentNode.removeChild(node);
25661             return;
25662         }
25663         //Roo.log(node.tagName);
25664         // remove - but keep children..
25665         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25666             //Roo.log('-- removed');
25667             while (node.childNodes.length) {
25668                 var cn = node.childNodes[0];
25669                 node.removeChild(cn);
25670                 node.parentNode.insertBefore(cn, node);
25671                 // move node to parent - and clean it..
25672                 this.cleanWord(cn);
25673             }
25674             node.parentNode.removeChild(node);
25675             /// no need to iterate chidlren = it's got none..
25676             //this.iterateChildren(node, this.cleanWord);
25677             return;
25678         }
25679         // clean styles
25680         if (node.className.length) {
25681             
25682             var cn = node.className.split(/\W+/);
25683             var cna = [];
25684             Roo.each(cn, function(cls) {
25685                 if (cls.match(/Mso[a-zA-Z]+/)) {
25686                     return;
25687                 }
25688                 cna.push(cls);
25689             });
25690             node.className = cna.length ? cna.join(' ') : '';
25691             if (!cna.length) {
25692                 node.removeAttribute("class");
25693             }
25694         }
25695         
25696         if (node.hasAttribute("lang")) {
25697             node.removeAttribute("lang");
25698         }
25699         
25700         if (node.hasAttribute("style")) {
25701             
25702             var styles = node.getAttribute("style").split(";");
25703             var nstyle = [];
25704             Roo.each(styles, function(s) {
25705                 if (!s.match(/:/)) {
25706                     return;
25707                 }
25708                 var kv = s.split(":");
25709                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25710                     return;
25711                 }
25712                 // what ever is left... we allow.
25713                 nstyle.push(s);
25714             });
25715             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25716             if (!nstyle.length) {
25717                 node.removeAttribute('style');
25718             }
25719         }
25720         this.iterateChildren(node, this.cleanWord);
25721         
25722         
25723         
25724     },
25725     /**
25726      * iterateChildren of a Node, calling fn each time, using this as the scole..
25727      * @param {DomNode} node node to iterate children of.
25728      * @param {Function} fn method of this class to call on each item.
25729      */
25730     iterateChildren : function(node, fn)
25731     {
25732         if (!node.childNodes.length) {
25733                 return;
25734         }
25735         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25736            fn.call(this, node.childNodes[i])
25737         }
25738     },
25739     
25740     
25741     /**
25742      * cleanTableWidths.
25743      *
25744      * Quite often pasting from word etc.. results in tables with column and widths.
25745      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25746      *
25747      */
25748     cleanTableWidths : function(node)
25749     {
25750          
25751          
25752         if (!node) {
25753             this.cleanTableWidths(this.doc.body);
25754             return;
25755         }
25756         
25757         // ignore list...
25758         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25759             return; 
25760         }
25761         Roo.log(node.tagName);
25762         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25763             this.iterateChildren(node, this.cleanTableWidths);
25764             return;
25765         }
25766         if (node.hasAttribute('width')) {
25767             node.removeAttribute('width');
25768         }
25769         
25770          
25771         if (node.hasAttribute("style")) {
25772             // pretty basic...
25773             
25774             var styles = node.getAttribute("style").split(";");
25775             var nstyle = [];
25776             Roo.each(styles, function(s) {
25777                 if (!s.match(/:/)) {
25778                     return;
25779                 }
25780                 var kv = s.split(":");
25781                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25782                     return;
25783                 }
25784                 // what ever is left... we allow.
25785                 nstyle.push(s);
25786             });
25787             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25788             if (!nstyle.length) {
25789                 node.removeAttribute('style');
25790             }
25791         }
25792         
25793         this.iterateChildren(node, this.cleanTableWidths);
25794         
25795         
25796     },
25797     
25798     
25799     
25800     
25801     domToHTML : function(currentElement, depth, nopadtext) {
25802         
25803         depth = depth || 0;
25804         nopadtext = nopadtext || false;
25805     
25806         if (!currentElement) {
25807             return this.domToHTML(this.doc.body);
25808         }
25809         
25810         //Roo.log(currentElement);
25811         var j;
25812         var allText = false;
25813         var nodeName = currentElement.nodeName;
25814         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25815         
25816         if  (nodeName == '#text') {
25817             
25818             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25819         }
25820         
25821         
25822         var ret = '';
25823         if (nodeName != 'BODY') {
25824              
25825             var i = 0;
25826             // Prints the node tagName, such as <A>, <IMG>, etc
25827             if (tagName) {
25828                 var attr = [];
25829                 for(i = 0; i < currentElement.attributes.length;i++) {
25830                     // quoting?
25831                     var aname = currentElement.attributes.item(i).name;
25832                     if (!currentElement.attributes.item(i).value.length) {
25833                         continue;
25834                     }
25835                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25836                 }
25837                 
25838                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25839             } 
25840             else {
25841                 
25842                 // eack
25843             }
25844         } else {
25845             tagName = false;
25846         }
25847         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25848             return ret;
25849         }
25850         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25851             nopadtext = true;
25852         }
25853         
25854         
25855         // Traverse the tree
25856         i = 0;
25857         var currentElementChild = currentElement.childNodes.item(i);
25858         var allText = true;
25859         var innerHTML  = '';
25860         lastnode = '';
25861         while (currentElementChild) {
25862             // Formatting code (indent the tree so it looks nice on the screen)
25863             var nopad = nopadtext;
25864             if (lastnode == 'SPAN') {
25865                 nopad  = true;
25866             }
25867             // text
25868             if  (currentElementChild.nodeName == '#text') {
25869                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25870                 toadd = nopadtext ? toadd : toadd.trim();
25871                 if (!nopad && toadd.length > 80) {
25872                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25873                 }
25874                 innerHTML  += toadd;
25875                 
25876                 i++;
25877                 currentElementChild = currentElement.childNodes.item(i);
25878                 lastNode = '';
25879                 continue;
25880             }
25881             allText = false;
25882             
25883             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25884                 
25885             // Recursively traverse the tree structure of the child node
25886             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25887             lastnode = currentElementChild.nodeName;
25888             i++;
25889             currentElementChild=currentElement.childNodes.item(i);
25890         }
25891         
25892         ret += innerHTML;
25893         
25894         if (!allText) {
25895                 // The remaining code is mostly for formatting the tree
25896             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25897         }
25898         
25899         
25900         if (tagName) {
25901             ret+= "</"+tagName+">";
25902         }
25903         return ret;
25904         
25905     },
25906         
25907     applyBlacklists : function()
25908     {
25909         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25910         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25911         
25912         this.white = [];
25913         this.black = [];
25914         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25915             if (b.indexOf(tag) > -1) {
25916                 return;
25917             }
25918             this.white.push(tag);
25919             
25920         }, this);
25921         
25922         Roo.each(w, function(tag) {
25923             if (b.indexOf(tag) > -1) {
25924                 return;
25925             }
25926             if (this.white.indexOf(tag) > -1) {
25927                 return;
25928             }
25929             this.white.push(tag);
25930             
25931         }, this);
25932         
25933         
25934         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25935             if (w.indexOf(tag) > -1) {
25936                 return;
25937             }
25938             this.black.push(tag);
25939             
25940         }, this);
25941         
25942         Roo.each(b, function(tag) {
25943             if (w.indexOf(tag) > -1) {
25944                 return;
25945             }
25946             if (this.black.indexOf(tag) > -1) {
25947                 return;
25948             }
25949             this.black.push(tag);
25950             
25951         }, this);
25952         
25953         
25954         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25955         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25956         
25957         this.cwhite = [];
25958         this.cblack = [];
25959         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25960             if (b.indexOf(tag) > -1) {
25961                 return;
25962             }
25963             this.cwhite.push(tag);
25964             
25965         }, this);
25966         
25967         Roo.each(w, function(tag) {
25968             if (b.indexOf(tag) > -1) {
25969                 return;
25970             }
25971             if (this.cwhite.indexOf(tag) > -1) {
25972                 return;
25973             }
25974             this.cwhite.push(tag);
25975             
25976         }, this);
25977         
25978         
25979         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25980             if (w.indexOf(tag) > -1) {
25981                 return;
25982             }
25983             this.cblack.push(tag);
25984             
25985         }, this);
25986         
25987         Roo.each(b, function(tag) {
25988             if (w.indexOf(tag) > -1) {
25989                 return;
25990             }
25991             if (this.cblack.indexOf(tag) > -1) {
25992                 return;
25993             }
25994             this.cblack.push(tag);
25995             
25996         }, this);
25997     },
25998     
25999     setStylesheets : function(stylesheets)
26000     {
26001         if(typeof(stylesheets) == 'string'){
26002             Roo.get(this.iframe.contentDocument.head).createChild({
26003                 tag : 'link',
26004                 rel : 'stylesheet',
26005                 type : 'text/css',
26006                 href : stylesheets
26007             });
26008             
26009             return;
26010         }
26011         var _this = this;
26012      
26013         Roo.each(stylesheets, function(s) {
26014             if(!s.length){
26015                 return;
26016             }
26017             
26018             Roo.get(_this.iframe.contentDocument.head).createChild({
26019                 tag : 'link',
26020                 rel : 'stylesheet',
26021                 type : 'text/css',
26022                 href : s
26023             });
26024         });
26025
26026         
26027     },
26028     
26029     removeStylesheets : function()
26030     {
26031         var _this = this;
26032         
26033         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26034             s.remove();
26035         });
26036     },
26037     
26038     setStyle : function(style)
26039     {
26040         Roo.get(this.iframe.contentDocument.head).createChild({
26041             tag : 'style',
26042             type : 'text/css',
26043             html : style
26044         });
26045
26046         return;
26047     }
26048     
26049     // hide stuff that is not compatible
26050     /**
26051      * @event blur
26052      * @hide
26053      */
26054     /**
26055      * @event change
26056      * @hide
26057      */
26058     /**
26059      * @event focus
26060      * @hide
26061      */
26062     /**
26063      * @event specialkey
26064      * @hide
26065      */
26066     /**
26067      * @cfg {String} fieldClass @hide
26068      */
26069     /**
26070      * @cfg {String} focusClass @hide
26071      */
26072     /**
26073      * @cfg {String} autoCreate @hide
26074      */
26075     /**
26076      * @cfg {String} inputType @hide
26077      */
26078     /**
26079      * @cfg {String} invalidClass @hide
26080      */
26081     /**
26082      * @cfg {String} invalidText @hide
26083      */
26084     /**
26085      * @cfg {String} msgFx @hide
26086      */
26087     /**
26088      * @cfg {String} validateOnBlur @hide
26089      */
26090 });
26091
26092 Roo.HtmlEditorCore.white = [
26093         'area', 'br', 'img', 'input', 'hr', 'wbr',
26094         
26095        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26096        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26097        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26098        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26099        'table',   'ul',         'xmp', 
26100        
26101        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26102       'thead',   'tr', 
26103      
26104       'dir', 'menu', 'ol', 'ul', 'dl',
26105        
26106       'embed',  'object'
26107 ];
26108
26109
26110 Roo.HtmlEditorCore.black = [
26111     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26112         'applet', // 
26113         'base',   'basefont', 'bgsound', 'blink',  'body', 
26114         'frame',  'frameset', 'head',    'html',   'ilayer', 
26115         'iframe', 'layer',  'link',     'meta',    'object',   
26116         'script', 'style' ,'title',  'xml' // clean later..
26117 ];
26118 Roo.HtmlEditorCore.clean = [
26119     'script', 'style', 'title', 'xml'
26120 ];
26121 Roo.HtmlEditorCore.remove = [
26122     'font'
26123 ];
26124 // attributes..
26125
26126 Roo.HtmlEditorCore.ablack = [
26127     'on'
26128 ];
26129     
26130 Roo.HtmlEditorCore.aclean = [ 
26131     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26132 ];
26133
26134 // protocols..
26135 Roo.HtmlEditorCore.pwhite= [
26136         'http',  'https',  'mailto'
26137 ];
26138
26139 // white listed style attributes.
26140 Roo.HtmlEditorCore.cwhite= [
26141       //  'text-align', /// default is to allow most things..
26142       
26143          
26144 //        'font-size'//??
26145 ];
26146
26147 // black listed style attributes.
26148 Roo.HtmlEditorCore.cblack= [
26149       //  'font-size' -- this can be set by the project 
26150 ];
26151
26152
26153 Roo.HtmlEditorCore.swapCodes   =[ 
26154     [    8211, "&#8211;" ], 
26155     [    8212, "&#8212;" ], 
26156     [    8216,  "'" ],  
26157     [    8217, "'" ],  
26158     [    8220, '"' ],  
26159     [    8221, '"' ],  
26160     [    8226, "*" ],  
26161     [    8230, "..." ]
26162 ]; 
26163
26164     /*
26165  * - LGPL
26166  *
26167  * HtmlEditor
26168  * 
26169  */
26170
26171 /**
26172  * @class Roo.bootstrap.HtmlEditor
26173  * @extends Roo.bootstrap.TextArea
26174  * Bootstrap HtmlEditor class
26175
26176  * @constructor
26177  * Create a new HtmlEditor
26178  * @param {Object} config The config object
26179  */
26180
26181 Roo.bootstrap.HtmlEditor = function(config){
26182     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26183     if (!this.toolbars) {
26184         this.toolbars = [];
26185     }
26186     
26187     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26188     this.addEvents({
26189             /**
26190              * @event initialize
26191              * Fires when the editor is fully initialized (including the iframe)
26192              * @param {HtmlEditor} this
26193              */
26194             initialize: true,
26195             /**
26196              * @event activate
26197              * Fires when the editor is first receives the focus. Any insertion must wait
26198              * until after this event.
26199              * @param {HtmlEditor} this
26200              */
26201             activate: true,
26202              /**
26203              * @event beforesync
26204              * Fires before the textarea is updated with content from the editor iframe. Return false
26205              * to cancel the sync.
26206              * @param {HtmlEditor} this
26207              * @param {String} html
26208              */
26209             beforesync: true,
26210              /**
26211              * @event beforepush
26212              * Fires before the iframe editor is updated with content from the textarea. Return false
26213              * to cancel the push.
26214              * @param {HtmlEditor} this
26215              * @param {String} html
26216              */
26217             beforepush: true,
26218              /**
26219              * @event sync
26220              * Fires when the textarea is updated with content from the editor iframe.
26221              * @param {HtmlEditor} this
26222              * @param {String} html
26223              */
26224             sync: true,
26225              /**
26226              * @event push
26227              * Fires when the iframe editor is updated with content from the textarea.
26228              * @param {HtmlEditor} this
26229              * @param {String} html
26230              */
26231             push: true,
26232              /**
26233              * @event editmodechange
26234              * Fires when the editor switches edit modes
26235              * @param {HtmlEditor} this
26236              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26237              */
26238             editmodechange: true,
26239             /**
26240              * @event editorevent
26241              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26242              * @param {HtmlEditor} this
26243              */
26244             editorevent: true,
26245             /**
26246              * @event firstfocus
26247              * Fires when on first focus - needed by toolbars..
26248              * @param {HtmlEditor} this
26249              */
26250             firstfocus: true,
26251             /**
26252              * @event autosave
26253              * Auto save the htmlEditor value as a file into Events
26254              * @param {HtmlEditor} this
26255              */
26256             autosave: true,
26257             /**
26258              * @event savedpreview
26259              * preview the saved version of htmlEditor
26260              * @param {HtmlEditor} this
26261              */
26262             savedpreview: true
26263         });
26264 };
26265
26266
26267 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26268     
26269     
26270       /**
26271      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26272      */
26273     toolbars : false,
26274     
26275      /**
26276     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26277     */
26278     btns : [],
26279    
26280      /**
26281      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26282      *                        Roo.resizable.
26283      */
26284     resizable : false,
26285      /**
26286      * @cfg {Number} height (in pixels)
26287      */   
26288     height: 300,
26289    /**
26290      * @cfg {Number} width (in pixels)
26291      */   
26292     width: false,
26293     
26294     /**
26295      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26296      * 
26297      */
26298     stylesheets: false,
26299     
26300     // id of frame..
26301     frameId: false,
26302     
26303     // private properties
26304     validationEvent : false,
26305     deferHeight: true,
26306     initialized : false,
26307     activated : false,
26308     
26309     onFocus : Roo.emptyFn,
26310     iframePad:3,
26311     hideMode:'offsets',
26312     
26313     tbContainer : false,
26314     
26315     bodyCls : '',
26316     
26317     toolbarContainer :function() {
26318         return this.wrap.select('.x-html-editor-tb',true).first();
26319     },
26320
26321     /**
26322      * Protected method that will not generally be called directly. It
26323      * is called when the editor creates its toolbar. Override this method if you need to
26324      * add custom toolbar buttons.
26325      * @param {HtmlEditor} editor
26326      */
26327     createToolbar : function(){
26328         Roo.log('renewing');
26329         Roo.log("create toolbars");
26330         
26331         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26332         this.toolbars[0].render(this.toolbarContainer());
26333         
26334         return;
26335         
26336 //        if (!editor.toolbars || !editor.toolbars.length) {
26337 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26338 //        }
26339 //        
26340 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26341 //            editor.toolbars[i] = Roo.factory(
26342 //                    typeof(editor.toolbars[i]) == 'string' ?
26343 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26344 //                Roo.bootstrap.HtmlEditor);
26345 //            editor.toolbars[i].init(editor);
26346 //        }
26347     },
26348
26349      
26350     // private
26351     onRender : function(ct, position)
26352     {
26353        // Roo.log("Call onRender: " + this.xtype);
26354         var _t = this;
26355         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26356       
26357         this.wrap = this.inputEl().wrap({
26358             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26359         });
26360         
26361         this.editorcore.onRender(ct, position);
26362          
26363         if (this.resizable) {
26364             this.resizeEl = new Roo.Resizable(this.wrap, {
26365                 pinned : true,
26366                 wrap: true,
26367                 dynamic : true,
26368                 minHeight : this.height,
26369                 height: this.height,
26370                 handles : this.resizable,
26371                 width: this.width,
26372                 listeners : {
26373                     resize : function(r, w, h) {
26374                         _t.onResize(w,h); // -something
26375                     }
26376                 }
26377             });
26378             
26379         }
26380         this.createToolbar(this);
26381        
26382         
26383         if(!this.width && this.resizable){
26384             this.setSize(this.wrap.getSize());
26385         }
26386         if (this.resizeEl) {
26387             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26388             // should trigger onReize..
26389         }
26390         
26391     },
26392
26393     // private
26394     onResize : function(w, h)
26395     {
26396         Roo.log('resize: ' +w + ',' + h );
26397         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26398         var ew = false;
26399         var eh = false;
26400         
26401         if(this.inputEl() ){
26402             if(typeof w == 'number'){
26403                 var aw = w - this.wrap.getFrameWidth('lr');
26404                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26405                 ew = aw;
26406             }
26407             if(typeof h == 'number'){
26408                  var tbh = -11;  // fixme it needs to tool bar size!
26409                 for (var i =0; i < this.toolbars.length;i++) {
26410                     // fixme - ask toolbars for heights?
26411                     tbh += this.toolbars[i].el.getHeight();
26412                     //if (this.toolbars[i].footer) {
26413                     //    tbh += this.toolbars[i].footer.el.getHeight();
26414                     //}
26415                 }
26416               
26417                 
26418                 
26419                 
26420                 
26421                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26422                 ah -= 5; // knock a few pixes off for look..
26423                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26424                 var eh = ah;
26425             }
26426         }
26427         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26428         this.editorcore.onResize(ew,eh);
26429         
26430     },
26431
26432     /**
26433      * Toggles the editor between standard and source edit mode.
26434      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26435      */
26436     toggleSourceEdit : function(sourceEditMode)
26437     {
26438         this.editorcore.toggleSourceEdit(sourceEditMode);
26439         
26440         if(this.editorcore.sourceEditMode){
26441             Roo.log('editor - showing textarea');
26442             
26443 //            Roo.log('in');
26444 //            Roo.log(this.syncValue());
26445             this.syncValue();
26446             this.inputEl().removeClass(['hide', 'x-hidden']);
26447             this.inputEl().dom.removeAttribute('tabIndex');
26448             this.inputEl().focus();
26449         }else{
26450             Roo.log('editor - hiding textarea');
26451 //            Roo.log('out')
26452 //            Roo.log(this.pushValue()); 
26453             this.pushValue();
26454             
26455             this.inputEl().addClass(['hide', 'x-hidden']);
26456             this.inputEl().dom.setAttribute('tabIndex', -1);
26457             //this.deferFocus();
26458         }
26459          
26460         if(this.resizable){
26461             this.setSize(this.wrap.getSize());
26462         }
26463         
26464         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26465     },
26466  
26467     // private (for BoxComponent)
26468     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26469
26470     // private (for BoxComponent)
26471     getResizeEl : function(){
26472         return this.wrap;
26473     },
26474
26475     // private (for BoxComponent)
26476     getPositionEl : function(){
26477         return this.wrap;
26478     },
26479
26480     // private
26481     initEvents : function(){
26482         this.originalValue = this.getValue();
26483     },
26484
26485 //    /**
26486 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26487 //     * @method
26488 //     */
26489 //    markInvalid : Roo.emptyFn,
26490 //    /**
26491 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26492 //     * @method
26493 //     */
26494 //    clearInvalid : Roo.emptyFn,
26495
26496     setValue : function(v){
26497         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26498         this.editorcore.pushValue();
26499     },
26500
26501      
26502     // private
26503     deferFocus : function(){
26504         this.focus.defer(10, this);
26505     },
26506
26507     // doc'ed in Field
26508     focus : function(){
26509         this.editorcore.focus();
26510         
26511     },
26512       
26513
26514     // private
26515     onDestroy : function(){
26516         
26517         
26518         
26519         if(this.rendered){
26520             
26521             for (var i =0; i < this.toolbars.length;i++) {
26522                 // fixme - ask toolbars for heights?
26523                 this.toolbars[i].onDestroy();
26524             }
26525             
26526             this.wrap.dom.innerHTML = '';
26527             this.wrap.remove();
26528         }
26529     },
26530
26531     // private
26532     onFirstFocus : function(){
26533         //Roo.log("onFirstFocus");
26534         this.editorcore.onFirstFocus();
26535          for (var i =0; i < this.toolbars.length;i++) {
26536             this.toolbars[i].onFirstFocus();
26537         }
26538         
26539     },
26540     
26541     // private
26542     syncValue : function()
26543     {   
26544         this.editorcore.syncValue();
26545     },
26546     
26547     pushValue : function()
26548     {   
26549         this.editorcore.pushValue();
26550     }
26551      
26552     
26553     // hide stuff that is not compatible
26554     /**
26555      * @event blur
26556      * @hide
26557      */
26558     /**
26559      * @event change
26560      * @hide
26561      */
26562     /**
26563      * @event focus
26564      * @hide
26565      */
26566     /**
26567      * @event specialkey
26568      * @hide
26569      */
26570     /**
26571      * @cfg {String} fieldClass @hide
26572      */
26573     /**
26574      * @cfg {String} focusClass @hide
26575      */
26576     /**
26577      * @cfg {String} autoCreate @hide
26578      */
26579     /**
26580      * @cfg {String} inputType @hide
26581      */
26582      
26583     /**
26584      * @cfg {String} invalidText @hide
26585      */
26586     /**
26587      * @cfg {String} msgFx @hide
26588      */
26589     /**
26590      * @cfg {String} validateOnBlur @hide
26591      */
26592 });
26593  
26594     
26595    
26596    
26597    
26598       
26599 Roo.namespace('Roo.bootstrap.htmleditor');
26600 /**
26601  * @class Roo.bootstrap.HtmlEditorToolbar1
26602  * Basic Toolbar
26603  * 
26604  * @example
26605  * Usage:
26606  *
26607  new Roo.bootstrap.HtmlEditor({
26608     ....
26609     toolbars : [
26610         new Roo.bootstrap.HtmlEditorToolbar1({
26611             disable : { fonts: 1 , format: 1, ..., ... , ...],
26612             btns : [ .... ]
26613         })
26614     }
26615      
26616  * 
26617  * @cfg {Object} disable List of elements to disable..
26618  * @cfg {Array} btns List of additional buttons.
26619  * 
26620  * 
26621  * NEEDS Extra CSS? 
26622  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26623  */
26624  
26625 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26626 {
26627     
26628     Roo.apply(this, config);
26629     
26630     // default disabled, based on 'good practice'..
26631     this.disable = this.disable || {};
26632     Roo.applyIf(this.disable, {
26633         fontSize : true,
26634         colors : true,
26635         specialElements : true
26636     });
26637     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26638     
26639     this.editor = config.editor;
26640     this.editorcore = config.editor.editorcore;
26641     
26642     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26643     
26644     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26645     // dont call parent... till later.
26646 }
26647 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26648      
26649     bar : true,
26650     
26651     editor : false,
26652     editorcore : false,
26653     
26654     
26655     formats : [
26656         "p" ,  
26657         "h1","h2","h3","h4","h5","h6", 
26658         "pre", "code", 
26659         "abbr", "acronym", "address", "cite", "samp", "var",
26660         'div','span'
26661     ],
26662     
26663     onRender : function(ct, position)
26664     {
26665        // Roo.log("Call onRender: " + this.xtype);
26666         
26667        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26668        Roo.log(this.el);
26669        this.el.dom.style.marginBottom = '0';
26670        var _this = this;
26671        var editorcore = this.editorcore;
26672        var editor= this.editor;
26673        
26674        var children = [];
26675        var btn = function(id,cmd , toggle, handler, html){
26676        
26677             var  event = toggle ? 'toggle' : 'click';
26678        
26679             var a = {
26680                 size : 'sm',
26681                 xtype: 'Button',
26682                 xns: Roo.bootstrap,
26683                 //glyphicon : id,
26684                 fa: id,
26685                 cmd : id || cmd,
26686                 enableToggle:toggle !== false,
26687                 html : html || '',
26688                 pressed : toggle ? false : null,
26689                 listeners : {}
26690             };
26691             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26692                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26693             };
26694             children.push(a);
26695             return a;
26696        }
26697        
26698     //    var cb_box = function...
26699         
26700         var style = {
26701                 xtype: 'Button',
26702                 size : 'sm',
26703                 xns: Roo.bootstrap,
26704                 fa : 'font',
26705                 //html : 'submit'
26706                 menu : {
26707                     xtype: 'Menu',
26708                     xns: Roo.bootstrap,
26709                     items:  []
26710                 }
26711         };
26712         Roo.each(this.formats, function(f) {
26713             style.menu.items.push({
26714                 xtype :'MenuItem',
26715                 xns: Roo.bootstrap,
26716                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26717                 tagname : f,
26718                 listeners : {
26719                     click : function()
26720                     {
26721                         editorcore.insertTag(this.tagname);
26722                         editor.focus();
26723                     }
26724                 }
26725                 
26726             });
26727         });
26728         children.push(style);   
26729         
26730         btn('bold',false,true);
26731         btn('italic',false,true);
26732         btn('align-left', 'justifyleft',true);
26733         btn('align-center', 'justifycenter',true);
26734         btn('align-right' , 'justifyright',true);
26735         btn('link', false, false, function(btn) {
26736             //Roo.log("create link?");
26737             var url = prompt(this.createLinkText, this.defaultLinkValue);
26738             if(url && url != 'http:/'+'/'){
26739                 this.editorcore.relayCmd('createlink', url);
26740             }
26741         }),
26742         btn('list','insertunorderedlist',true);
26743         btn('pencil', false,true, function(btn){
26744                 Roo.log(this);
26745                 this.toggleSourceEdit(btn.pressed);
26746         });
26747         
26748         if (this.editor.btns.length > 0) {
26749             for (var i = 0; i<this.editor.btns.length; i++) {
26750                 children.push(this.editor.btns[i]);
26751             }
26752         }
26753         
26754         /*
26755         var cog = {
26756                 xtype: 'Button',
26757                 size : 'sm',
26758                 xns: Roo.bootstrap,
26759                 glyphicon : 'cog',
26760                 //html : 'submit'
26761                 menu : {
26762                     xtype: 'Menu',
26763                     xns: Roo.bootstrap,
26764                     items:  []
26765                 }
26766         };
26767         
26768         cog.menu.items.push({
26769             xtype :'MenuItem',
26770             xns: Roo.bootstrap,
26771             html : Clean styles,
26772             tagname : f,
26773             listeners : {
26774                 click : function()
26775                 {
26776                     editorcore.insertTag(this.tagname);
26777                     editor.focus();
26778                 }
26779             }
26780             
26781         });
26782        */
26783         
26784          
26785        this.xtype = 'NavSimplebar';
26786         
26787         for(var i=0;i< children.length;i++) {
26788             
26789             this.buttons.add(this.addxtypeChild(children[i]));
26790             
26791         }
26792         
26793         editor.on('editorevent', this.updateToolbar, this);
26794     },
26795     onBtnClick : function(id)
26796     {
26797        this.editorcore.relayCmd(id);
26798        this.editorcore.focus();
26799     },
26800     
26801     /**
26802      * Protected method that will not generally be called directly. It triggers
26803      * a toolbar update by reading the markup state of the current selection in the editor.
26804      */
26805     updateToolbar: function(){
26806
26807         if(!this.editorcore.activated){
26808             this.editor.onFirstFocus(); // is this neeed?
26809             return;
26810         }
26811
26812         var btns = this.buttons; 
26813         var doc = this.editorcore.doc;
26814         btns.get('bold').setActive(doc.queryCommandState('bold'));
26815         btns.get('italic').setActive(doc.queryCommandState('italic'));
26816         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26817         
26818         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26819         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26820         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26821         
26822         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26823         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26824          /*
26825         
26826         var ans = this.editorcore.getAllAncestors();
26827         if (this.formatCombo) {
26828             
26829             
26830             var store = this.formatCombo.store;
26831             this.formatCombo.setValue("");
26832             for (var i =0; i < ans.length;i++) {
26833                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26834                     // select it..
26835                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26836                     break;
26837                 }
26838             }
26839         }
26840         
26841         
26842         
26843         // hides menus... - so this cant be on a menu...
26844         Roo.bootstrap.MenuMgr.hideAll();
26845         */
26846         Roo.bootstrap.MenuMgr.hideAll();
26847         //this.editorsyncValue();
26848     },
26849     onFirstFocus: function() {
26850         this.buttons.each(function(item){
26851            item.enable();
26852         });
26853     },
26854     toggleSourceEdit : function(sourceEditMode){
26855         
26856           
26857         if(sourceEditMode){
26858             Roo.log("disabling buttons");
26859            this.buttons.each( function(item){
26860                 if(item.cmd != 'pencil'){
26861                     item.disable();
26862                 }
26863             });
26864           
26865         }else{
26866             Roo.log("enabling buttons");
26867             if(this.editorcore.initialized){
26868                 this.buttons.each( function(item){
26869                     item.enable();
26870                 });
26871             }
26872             
26873         }
26874         Roo.log("calling toggole on editor");
26875         // tell the editor that it's been pressed..
26876         this.editor.toggleSourceEdit(sourceEditMode);
26877        
26878     }
26879 });
26880
26881
26882
26883
26884  
26885 /*
26886  * - LGPL
26887  */
26888
26889 /**
26890  * @class Roo.bootstrap.Markdown
26891  * @extends Roo.bootstrap.TextArea
26892  * Bootstrap Showdown editable area
26893  * @cfg {string} content
26894  * 
26895  * @constructor
26896  * Create a new Showdown
26897  */
26898
26899 Roo.bootstrap.Markdown = function(config){
26900     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26901    
26902 };
26903
26904 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26905     
26906     editing :false,
26907     
26908     initEvents : function()
26909     {
26910         
26911         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26912         this.markdownEl = this.el.createChild({
26913             cls : 'roo-markdown-area'
26914         });
26915         this.inputEl().addClass('d-none');
26916         if (this.getValue() == '') {
26917             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26918             
26919         } else {
26920             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26921         }
26922         this.markdownEl.on('click', this.toggleTextEdit, this);
26923         this.on('blur', this.toggleTextEdit, this);
26924         this.on('specialkey', this.resizeTextArea, this);
26925     },
26926     
26927     toggleTextEdit : function()
26928     {
26929         var sh = this.markdownEl.getHeight();
26930         this.inputEl().addClass('d-none');
26931         this.markdownEl.addClass('d-none');
26932         if (!this.editing) {
26933             // show editor?
26934             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26935             this.inputEl().removeClass('d-none');
26936             this.inputEl().focus();
26937             this.editing = true;
26938             return;
26939         }
26940         // show showdown...
26941         this.updateMarkdown();
26942         this.markdownEl.removeClass('d-none');
26943         this.editing = false;
26944         return;
26945     },
26946     updateMarkdown : function()
26947     {
26948         if (this.getValue() == '') {
26949             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26950             return;
26951         }
26952  
26953         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26954     },
26955     
26956     resizeTextArea: function () {
26957         
26958         var sh = 100;
26959         Roo.log([sh, this.getValue().split("\n").length * 30]);
26960         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26961     },
26962     setValue : function(val)
26963     {
26964         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26965         if (!this.editing) {
26966             this.updateMarkdown();
26967         }
26968         
26969     },
26970     focus : function()
26971     {
26972         if (!this.editing) {
26973             this.toggleTextEdit();
26974         }
26975         
26976     }
26977
26978
26979 });
26980 /**
26981  * @class Roo.bootstrap.Table.AbstractSelectionModel
26982  * @extends Roo.util.Observable
26983  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26984  * implemented by descendant classes.  This class should not be directly instantiated.
26985  * @constructor
26986  */
26987 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26988     this.locked = false;
26989     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26990 };
26991
26992
26993 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26994     /** @ignore Called by the grid automatically. Do not call directly. */
26995     init : function(grid){
26996         this.grid = grid;
26997         this.initEvents();
26998     },
26999
27000     /**
27001      * Locks the selections.
27002      */
27003     lock : function(){
27004         this.locked = true;
27005     },
27006
27007     /**
27008      * Unlocks the selections.
27009      */
27010     unlock : function(){
27011         this.locked = false;
27012     },
27013
27014     /**
27015      * Returns true if the selections are locked.
27016      * @return {Boolean}
27017      */
27018     isLocked : function(){
27019         return this.locked;
27020     },
27021     
27022     
27023     initEvents : function ()
27024     {
27025         
27026     }
27027 });
27028 /**
27029  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27030  * @class Roo.bootstrap.Table.RowSelectionModel
27031  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27032  * It supports multiple selections and keyboard selection/navigation. 
27033  * @constructor
27034  * @param {Object} config
27035  */
27036
27037 Roo.bootstrap.Table.RowSelectionModel = function(config){
27038     Roo.apply(this, config);
27039     this.selections = new Roo.util.MixedCollection(false, function(o){
27040         return o.id;
27041     });
27042
27043     this.last = false;
27044     this.lastActive = false;
27045
27046     this.addEvents({
27047         /**
27048              * @event selectionchange
27049              * Fires when the selection changes
27050              * @param {SelectionModel} this
27051              */
27052             "selectionchange" : true,
27053         /**
27054              * @event afterselectionchange
27055              * Fires after the selection changes (eg. by key press or clicking)
27056              * @param {SelectionModel} this
27057              */
27058             "afterselectionchange" : true,
27059         /**
27060              * @event beforerowselect
27061              * Fires when a row is selected being selected, return false to cancel.
27062              * @param {SelectionModel} this
27063              * @param {Number} rowIndex The selected index
27064              * @param {Boolean} keepExisting False if other selections will be cleared
27065              */
27066             "beforerowselect" : true,
27067         /**
27068              * @event rowselect
27069              * Fires when a row is selected.
27070              * @param {SelectionModel} this
27071              * @param {Number} rowIndex The selected index
27072              * @param {Roo.data.Record} r The record
27073              */
27074             "rowselect" : true,
27075         /**
27076              * @event rowdeselect
27077              * Fires when a row is deselected.
27078              * @param {SelectionModel} this
27079              * @param {Number} rowIndex The selected index
27080              */
27081         "rowdeselect" : true
27082     });
27083     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27084     this.locked = false;
27085  };
27086
27087 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27088     /**
27089      * @cfg {Boolean} singleSelect
27090      * True to allow selection of only one row at a time (defaults to false)
27091      */
27092     singleSelect : false,
27093
27094     // private
27095     initEvents : function()
27096     {
27097
27098         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27099         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27100         //}else{ // allow click to work like normal
27101          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27102         //}
27103         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27104         this.grid.on("rowclick", this.handleMouseDown, this);
27105         
27106         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27107             "up" : function(e){
27108                 if(!e.shiftKey){
27109                     this.selectPrevious(e.shiftKey);
27110                 }else if(this.last !== false && this.lastActive !== false){
27111                     var last = this.last;
27112                     this.selectRange(this.last,  this.lastActive-1);
27113                     this.grid.getView().focusRow(this.lastActive);
27114                     if(last !== false){
27115                         this.last = last;
27116                     }
27117                 }else{
27118                     this.selectFirstRow();
27119                 }
27120                 this.fireEvent("afterselectionchange", this);
27121             },
27122             "down" : function(e){
27123                 if(!e.shiftKey){
27124                     this.selectNext(e.shiftKey);
27125                 }else if(this.last !== false && this.lastActive !== false){
27126                     var last = this.last;
27127                     this.selectRange(this.last,  this.lastActive+1);
27128                     this.grid.getView().focusRow(this.lastActive);
27129                     if(last !== false){
27130                         this.last = last;
27131                     }
27132                 }else{
27133                     this.selectFirstRow();
27134                 }
27135                 this.fireEvent("afterselectionchange", this);
27136             },
27137             scope: this
27138         });
27139         this.grid.store.on('load', function(){
27140             this.selections.clear();
27141         },this);
27142         /*
27143         var view = this.grid.view;
27144         view.on("refresh", this.onRefresh, this);
27145         view.on("rowupdated", this.onRowUpdated, this);
27146         view.on("rowremoved", this.onRemove, this);
27147         */
27148     },
27149
27150     // private
27151     onRefresh : function()
27152     {
27153         var ds = this.grid.store, i, v = this.grid.view;
27154         var s = this.selections;
27155         s.each(function(r){
27156             if((i = ds.indexOfId(r.id)) != -1){
27157                 v.onRowSelect(i);
27158             }else{
27159                 s.remove(r);
27160             }
27161         });
27162     },
27163
27164     // private
27165     onRemove : function(v, index, r){
27166         this.selections.remove(r);
27167     },
27168
27169     // private
27170     onRowUpdated : function(v, index, r){
27171         if(this.isSelected(r)){
27172             v.onRowSelect(index);
27173         }
27174     },
27175
27176     /**
27177      * Select records.
27178      * @param {Array} records The records to select
27179      * @param {Boolean} keepExisting (optional) True to keep existing selections
27180      */
27181     selectRecords : function(records, keepExisting)
27182     {
27183         if(!keepExisting){
27184             this.clearSelections();
27185         }
27186             var ds = this.grid.store;
27187         for(var i = 0, len = records.length; i < len; i++){
27188             this.selectRow(ds.indexOf(records[i]), true);
27189         }
27190     },
27191
27192     /**
27193      * Gets the number of selected rows.
27194      * @return {Number}
27195      */
27196     getCount : function(){
27197         return this.selections.length;
27198     },
27199
27200     /**
27201      * Selects the first row in the grid.
27202      */
27203     selectFirstRow : function(){
27204         this.selectRow(0);
27205     },
27206
27207     /**
27208      * Select the last row.
27209      * @param {Boolean} keepExisting (optional) True to keep existing selections
27210      */
27211     selectLastRow : function(keepExisting){
27212         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27213         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27214     },
27215
27216     /**
27217      * Selects the row immediately following the last selected row.
27218      * @param {Boolean} keepExisting (optional) True to keep existing selections
27219      */
27220     selectNext : function(keepExisting)
27221     {
27222             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27223             this.selectRow(this.last+1, keepExisting);
27224             this.grid.getView().focusRow(this.last);
27225         }
27226     },
27227
27228     /**
27229      * Selects the row that precedes the last selected row.
27230      * @param {Boolean} keepExisting (optional) True to keep existing selections
27231      */
27232     selectPrevious : function(keepExisting){
27233         if(this.last){
27234             this.selectRow(this.last-1, keepExisting);
27235             this.grid.getView().focusRow(this.last);
27236         }
27237     },
27238
27239     /**
27240      * Returns the selected records
27241      * @return {Array} Array of selected records
27242      */
27243     getSelections : function(){
27244         return [].concat(this.selections.items);
27245     },
27246
27247     /**
27248      * Returns the first selected record.
27249      * @return {Record}
27250      */
27251     getSelected : function(){
27252         return this.selections.itemAt(0);
27253     },
27254
27255
27256     /**
27257      * Clears all selections.
27258      */
27259     clearSelections : function(fast)
27260     {
27261         if(this.locked) {
27262             return;
27263         }
27264         if(fast !== true){
27265                 var ds = this.grid.store;
27266             var s = this.selections;
27267             s.each(function(r){
27268                 this.deselectRow(ds.indexOfId(r.id));
27269             }, this);
27270             s.clear();
27271         }else{
27272             this.selections.clear();
27273         }
27274         this.last = false;
27275     },
27276
27277
27278     /**
27279      * Selects all rows.
27280      */
27281     selectAll : function(){
27282         if(this.locked) {
27283             return;
27284         }
27285         this.selections.clear();
27286         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27287             this.selectRow(i, true);
27288         }
27289     },
27290
27291     /**
27292      * Returns True if there is a selection.
27293      * @return {Boolean}
27294      */
27295     hasSelection : function(){
27296         return this.selections.length > 0;
27297     },
27298
27299     /**
27300      * Returns True if the specified row is selected.
27301      * @param {Number/Record} record The record or index of the record to check
27302      * @return {Boolean}
27303      */
27304     isSelected : function(index){
27305             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27306         return (r && this.selections.key(r.id) ? true : false);
27307     },
27308
27309     /**
27310      * Returns True if the specified record id is selected.
27311      * @param {String} id The id of record to check
27312      * @return {Boolean}
27313      */
27314     isIdSelected : function(id){
27315         return (this.selections.key(id) ? true : false);
27316     },
27317
27318
27319     // private
27320     handleMouseDBClick : function(e, t){
27321         
27322     },
27323     // private
27324     handleMouseDown : function(e, t)
27325     {
27326             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27327         if(this.isLocked() || rowIndex < 0 ){
27328             return;
27329         };
27330         if(e.shiftKey && this.last !== false){
27331             var last = this.last;
27332             this.selectRange(last, rowIndex, e.ctrlKey);
27333             this.last = last; // reset the last
27334             t.focus();
27335     
27336         }else{
27337             var isSelected = this.isSelected(rowIndex);
27338             //Roo.log("select row:" + rowIndex);
27339             if(isSelected){
27340                 this.deselectRow(rowIndex);
27341             } else {
27342                         this.selectRow(rowIndex, true);
27343             }
27344     
27345             /*
27346                 if(e.button !== 0 && isSelected){
27347                 alert('rowIndex 2: ' + rowIndex);
27348                     view.focusRow(rowIndex);
27349                 }else if(e.ctrlKey && isSelected){
27350                     this.deselectRow(rowIndex);
27351                 }else if(!isSelected){
27352                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27353                     view.focusRow(rowIndex);
27354                 }
27355             */
27356         }
27357         this.fireEvent("afterselectionchange", this);
27358     },
27359     // private
27360     handleDragableRowClick :  function(grid, rowIndex, e) 
27361     {
27362         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27363             this.selectRow(rowIndex, false);
27364             grid.view.focusRow(rowIndex);
27365              this.fireEvent("afterselectionchange", this);
27366         }
27367     },
27368     
27369     /**
27370      * Selects multiple rows.
27371      * @param {Array} rows Array of the indexes of the row to select
27372      * @param {Boolean} keepExisting (optional) True to keep existing selections
27373      */
27374     selectRows : function(rows, keepExisting){
27375         if(!keepExisting){
27376             this.clearSelections();
27377         }
27378         for(var i = 0, len = rows.length; i < len; i++){
27379             this.selectRow(rows[i], true);
27380         }
27381     },
27382
27383     /**
27384      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27385      * @param {Number} startRow The index of the first row in the range
27386      * @param {Number} endRow The index of the last row in the range
27387      * @param {Boolean} keepExisting (optional) True to retain existing selections
27388      */
27389     selectRange : function(startRow, endRow, keepExisting){
27390         if(this.locked) {
27391             return;
27392         }
27393         if(!keepExisting){
27394             this.clearSelections();
27395         }
27396         if(startRow <= endRow){
27397             for(var i = startRow; i <= endRow; i++){
27398                 this.selectRow(i, true);
27399             }
27400         }else{
27401             for(var i = startRow; i >= endRow; i--){
27402                 this.selectRow(i, true);
27403             }
27404         }
27405     },
27406
27407     /**
27408      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27409      * @param {Number} startRow The index of the first row in the range
27410      * @param {Number} endRow The index of the last row in the range
27411      */
27412     deselectRange : function(startRow, endRow, preventViewNotify){
27413         if(this.locked) {
27414             return;
27415         }
27416         for(var i = startRow; i <= endRow; i++){
27417             this.deselectRow(i, preventViewNotify);
27418         }
27419     },
27420
27421     /**
27422      * Selects a row.
27423      * @param {Number} row The index of the row to select
27424      * @param {Boolean} keepExisting (optional) True to keep existing selections
27425      */
27426     selectRow : function(index, keepExisting, preventViewNotify)
27427     {
27428             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27429             return;
27430         }
27431         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27432             if(!keepExisting || this.singleSelect){
27433                 this.clearSelections();
27434             }
27435             
27436             var r = this.grid.store.getAt(index);
27437             //console.log('selectRow - record id :' + r.id);
27438             
27439             this.selections.add(r);
27440             this.last = this.lastActive = index;
27441             if(!preventViewNotify){
27442                 var proxy = new Roo.Element(
27443                                 this.grid.getRowDom(index)
27444                 );
27445                 proxy.addClass('bg-info info');
27446             }
27447             this.fireEvent("rowselect", this, index, r);
27448             this.fireEvent("selectionchange", this);
27449         }
27450     },
27451
27452     /**
27453      * Deselects a row.
27454      * @param {Number} row The index of the row to deselect
27455      */
27456     deselectRow : function(index, preventViewNotify)
27457     {
27458         if(this.locked) {
27459             return;
27460         }
27461         if(this.last == index){
27462             this.last = false;
27463         }
27464         if(this.lastActive == index){
27465             this.lastActive = false;
27466         }
27467         
27468         var r = this.grid.store.getAt(index);
27469         if (!r) {
27470             return;
27471         }
27472         
27473         this.selections.remove(r);
27474         //.console.log('deselectRow - record id :' + r.id);
27475         if(!preventViewNotify){
27476         
27477             var proxy = new Roo.Element(
27478                 this.grid.getRowDom(index)
27479             );
27480             proxy.removeClass('bg-info info');
27481         }
27482         this.fireEvent("rowdeselect", this, index);
27483         this.fireEvent("selectionchange", this);
27484     },
27485
27486     // private
27487     restoreLast : function(){
27488         if(this._last){
27489             this.last = this._last;
27490         }
27491     },
27492
27493     // private
27494     acceptsNav : function(row, col, cm){
27495         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27496     },
27497
27498     // private
27499     onEditorKey : function(field, e){
27500         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27501         if(k == e.TAB){
27502             e.stopEvent();
27503             ed.completeEdit();
27504             if(e.shiftKey){
27505                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27506             }else{
27507                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27508             }
27509         }else if(k == e.ENTER && !e.ctrlKey){
27510             e.stopEvent();
27511             ed.completeEdit();
27512             if(e.shiftKey){
27513                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27514             }else{
27515                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27516             }
27517         }else if(k == e.ESC){
27518             ed.cancelEdit();
27519         }
27520         if(newCell){
27521             g.startEditing(newCell[0], newCell[1]);
27522         }
27523     }
27524 });
27525 /*
27526  * Based on:
27527  * Ext JS Library 1.1.1
27528  * Copyright(c) 2006-2007, Ext JS, LLC.
27529  *
27530  * Originally Released Under LGPL - original licence link has changed is not relivant.
27531  *
27532  * Fork - LGPL
27533  * <script type="text/javascript">
27534  */
27535  
27536 /**
27537  * @class Roo.bootstrap.PagingToolbar
27538  * @extends Roo.bootstrap.NavSimplebar
27539  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27540  * @constructor
27541  * Create a new PagingToolbar
27542  * @param {Object} config The config object
27543  * @param {Roo.data.Store} store
27544  */
27545 Roo.bootstrap.PagingToolbar = function(config)
27546 {
27547     // old args format still supported... - xtype is prefered..
27548         // created from xtype...
27549     
27550     this.ds = config.dataSource;
27551     
27552     if (config.store && !this.ds) {
27553         this.store= Roo.factory(config.store, Roo.data);
27554         this.ds = this.store;
27555         this.ds.xmodule = this.xmodule || false;
27556     }
27557     
27558     this.toolbarItems = [];
27559     if (config.items) {
27560         this.toolbarItems = config.items;
27561     }
27562     
27563     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27564     
27565     this.cursor = 0;
27566     
27567     if (this.ds) { 
27568         this.bind(this.ds);
27569     }
27570     
27571     if (Roo.bootstrap.version == 4) {
27572         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27573     } else {
27574         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27575     }
27576     
27577 };
27578
27579 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27580     /**
27581      * @cfg {Roo.data.Store} dataSource
27582      * The underlying data store providing the paged data
27583      */
27584     /**
27585      * @cfg {String/HTMLElement/Element} container
27586      * container The id or element that will contain the toolbar
27587      */
27588     /**
27589      * @cfg {Boolean} displayInfo
27590      * True to display the displayMsg (defaults to false)
27591      */
27592     /**
27593      * @cfg {Number} pageSize
27594      * The number of records to display per page (defaults to 20)
27595      */
27596     pageSize: 20,
27597     /**
27598      * @cfg {String} displayMsg
27599      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27600      */
27601     displayMsg : 'Displaying {0} - {1} of {2}',
27602     /**
27603      * @cfg {String} emptyMsg
27604      * The message to display when no records are found (defaults to "No data to display")
27605      */
27606     emptyMsg : 'No data to display',
27607     /**
27608      * Customizable piece of the default paging text (defaults to "Page")
27609      * @type String
27610      */
27611     beforePageText : "Page",
27612     /**
27613      * Customizable piece of the default paging text (defaults to "of %0")
27614      * @type String
27615      */
27616     afterPageText : "of {0}",
27617     /**
27618      * Customizable piece of the default paging text (defaults to "First Page")
27619      * @type String
27620      */
27621     firstText : "First Page",
27622     /**
27623      * Customizable piece of the default paging text (defaults to "Previous Page")
27624      * @type String
27625      */
27626     prevText : "Previous Page",
27627     /**
27628      * Customizable piece of the default paging text (defaults to "Next Page")
27629      * @type String
27630      */
27631     nextText : "Next Page",
27632     /**
27633      * Customizable piece of the default paging text (defaults to "Last Page")
27634      * @type String
27635      */
27636     lastText : "Last Page",
27637     /**
27638      * Customizable piece of the default paging text (defaults to "Refresh")
27639      * @type String
27640      */
27641     refreshText : "Refresh",
27642
27643     buttons : false,
27644     // private
27645     onRender : function(ct, position) 
27646     {
27647         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27648         this.navgroup.parentId = this.id;
27649         this.navgroup.onRender(this.el, null);
27650         // add the buttons to the navgroup
27651         
27652         if(this.displayInfo){
27653             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27654             this.displayEl = this.el.select('.x-paging-info', true).first();
27655 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27656 //            this.displayEl = navel.el.select('span',true).first();
27657         }
27658         
27659         var _this = this;
27660         
27661         if(this.buttons){
27662             Roo.each(_this.buttons, function(e){ // this might need to use render????
27663                Roo.factory(e).render(_this.el);
27664             });
27665         }
27666             
27667         Roo.each(_this.toolbarItems, function(e) {
27668             _this.navgroup.addItem(e);
27669         });
27670         
27671         
27672         this.first = this.navgroup.addItem({
27673             tooltip: this.firstText,
27674             cls: "prev btn-outline-secondary",
27675             html : ' <i class="fa fa-step-backward"></i>',
27676             disabled: true,
27677             preventDefault: true,
27678             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27679         });
27680         
27681         this.prev =  this.navgroup.addItem({
27682             tooltip: this.prevText,
27683             cls: "prev btn-outline-secondary",
27684             html : ' <i class="fa fa-backward"></i>',
27685             disabled: true,
27686             preventDefault: true,
27687             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27688         });
27689     //this.addSeparator();
27690         
27691         
27692         var field = this.navgroup.addItem( {
27693             tagtype : 'span',
27694             cls : 'x-paging-position  btn-outline-secondary',
27695              disabled: true,
27696             html : this.beforePageText  +
27697                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27698                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27699          } ); //?? escaped?
27700         
27701         this.field = field.el.select('input', true).first();
27702         this.field.on("keydown", this.onPagingKeydown, this);
27703         this.field.on("focus", function(){this.dom.select();});
27704     
27705     
27706         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27707         //this.field.setHeight(18);
27708         //this.addSeparator();
27709         this.next = this.navgroup.addItem({
27710             tooltip: this.nextText,
27711             cls: "next btn-outline-secondary",
27712             html : ' <i class="fa fa-forward"></i>',
27713             disabled: true,
27714             preventDefault: true,
27715             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27716         });
27717         this.last = this.navgroup.addItem({
27718             tooltip: this.lastText,
27719             html : ' <i class="fa fa-step-forward"></i>',
27720             cls: "next btn-outline-secondary",
27721             disabled: true,
27722             preventDefault: true,
27723             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27724         });
27725     //this.addSeparator();
27726         this.loading = this.navgroup.addItem({
27727             tooltip: this.refreshText,
27728             cls: "btn-outline-secondary",
27729             html : ' <i class="fa fa-refresh"></i>',
27730             preventDefault: true,
27731             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27732         });
27733         
27734     },
27735
27736     // private
27737     updateInfo : function(){
27738         if(this.displayEl){
27739             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27740             var msg = count == 0 ?
27741                 this.emptyMsg :
27742                 String.format(
27743                     this.displayMsg,
27744                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27745                 );
27746             this.displayEl.update(msg);
27747         }
27748     },
27749
27750     // private
27751     onLoad : function(ds, r, o)
27752     {
27753         this.cursor = o.params && o.params.start ? o.params.start : 0;
27754         
27755         var d = this.getPageData(),
27756             ap = d.activePage,
27757             ps = d.pages;
27758         
27759         
27760         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27761         this.field.dom.value = ap;
27762         this.first.setDisabled(ap == 1);
27763         this.prev.setDisabled(ap == 1);
27764         this.next.setDisabled(ap == ps);
27765         this.last.setDisabled(ap == ps);
27766         this.loading.enable();
27767         this.updateInfo();
27768     },
27769
27770     // private
27771     getPageData : function(){
27772         var total = this.ds.getTotalCount();
27773         return {
27774             total : total,
27775             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27776             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27777         };
27778     },
27779
27780     // private
27781     onLoadError : function(){
27782         this.loading.enable();
27783     },
27784
27785     // private
27786     onPagingKeydown : function(e){
27787         var k = e.getKey();
27788         var d = this.getPageData();
27789         if(k == e.RETURN){
27790             var v = this.field.dom.value, pageNum;
27791             if(!v || isNaN(pageNum = parseInt(v, 10))){
27792                 this.field.dom.value = d.activePage;
27793                 return;
27794             }
27795             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27796             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27797             e.stopEvent();
27798         }
27799         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))
27800         {
27801           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27802           this.field.dom.value = pageNum;
27803           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27804           e.stopEvent();
27805         }
27806         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27807         {
27808           var v = this.field.dom.value, pageNum; 
27809           var increment = (e.shiftKey) ? 10 : 1;
27810           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27811                 increment *= -1;
27812           }
27813           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27814             this.field.dom.value = d.activePage;
27815             return;
27816           }
27817           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27818           {
27819             this.field.dom.value = parseInt(v, 10) + increment;
27820             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27821             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27822           }
27823           e.stopEvent();
27824         }
27825     },
27826
27827     // private
27828     beforeLoad : function(){
27829         if(this.loading){
27830             this.loading.disable();
27831         }
27832     },
27833
27834     // private
27835     onClick : function(which){
27836         
27837         var ds = this.ds;
27838         if (!ds) {
27839             return;
27840         }
27841         
27842         switch(which){
27843             case "first":
27844                 ds.load({params:{start: 0, limit: this.pageSize}});
27845             break;
27846             case "prev":
27847                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27848             break;
27849             case "next":
27850                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27851             break;
27852             case "last":
27853                 var total = ds.getTotalCount();
27854                 var extra = total % this.pageSize;
27855                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27856                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27857             break;
27858             case "refresh":
27859                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27860             break;
27861         }
27862     },
27863
27864     /**
27865      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27866      * @param {Roo.data.Store} store The data store to unbind
27867      */
27868     unbind : function(ds){
27869         ds.un("beforeload", this.beforeLoad, this);
27870         ds.un("load", this.onLoad, this);
27871         ds.un("loadexception", this.onLoadError, this);
27872         ds.un("remove", this.updateInfo, this);
27873         ds.un("add", this.updateInfo, this);
27874         this.ds = undefined;
27875     },
27876
27877     /**
27878      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27879      * @param {Roo.data.Store} store The data store to bind
27880      */
27881     bind : function(ds){
27882         ds.on("beforeload", this.beforeLoad, this);
27883         ds.on("load", this.onLoad, this);
27884         ds.on("loadexception", this.onLoadError, this);
27885         ds.on("remove", this.updateInfo, this);
27886         ds.on("add", this.updateInfo, this);
27887         this.ds = ds;
27888     }
27889 });/*
27890  * - LGPL
27891  *
27892  * element
27893  * 
27894  */
27895
27896 /**
27897  * @class Roo.bootstrap.MessageBar
27898  * @extends Roo.bootstrap.Component
27899  * Bootstrap MessageBar class
27900  * @cfg {String} html contents of the MessageBar
27901  * @cfg {String} weight (info | success | warning | danger) default info
27902  * @cfg {String} beforeClass insert the bar before the given class
27903  * @cfg {Boolean} closable (true | false) default false
27904  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27905  * 
27906  * @constructor
27907  * Create a new Element
27908  * @param {Object} config The config object
27909  */
27910
27911 Roo.bootstrap.MessageBar = function(config){
27912     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27913 };
27914
27915 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27916     
27917     html: '',
27918     weight: 'info',
27919     closable: false,
27920     fixed: false,
27921     beforeClass: 'bootstrap-sticky-wrap',
27922     
27923     getAutoCreate : function(){
27924         
27925         var cfg = {
27926             tag: 'div',
27927             cls: 'alert alert-dismissable alert-' + this.weight,
27928             cn: [
27929                 {
27930                     tag: 'span',
27931                     cls: 'message',
27932                     html: this.html || ''
27933                 }
27934             ]
27935         };
27936         
27937         if(this.fixed){
27938             cfg.cls += ' alert-messages-fixed';
27939         }
27940         
27941         if(this.closable){
27942             cfg.cn.push({
27943                 tag: 'button',
27944                 cls: 'close',
27945                 html: 'x'
27946             });
27947         }
27948         
27949         return cfg;
27950     },
27951     
27952     onRender : function(ct, position)
27953     {
27954         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27955         
27956         if(!this.el){
27957             var cfg = Roo.apply({},  this.getAutoCreate());
27958             cfg.id = Roo.id();
27959             
27960             if (this.cls) {
27961                 cfg.cls += ' ' + this.cls;
27962             }
27963             if (this.style) {
27964                 cfg.style = this.style;
27965             }
27966             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27967             
27968             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27969         }
27970         
27971         this.el.select('>button.close').on('click', this.hide, this);
27972         
27973     },
27974     
27975     show : function()
27976     {
27977         if (!this.rendered) {
27978             this.render();
27979         }
27980         
27981         this.el.show();
27982         
27983         this.fireEvent('show', this);
27984         
27985     },
27986     
27987     hide : function()
27988     {
27989         if (!this.rendered) {
27990             this.render();
27991         }
27992         
27993         this.el.hide();
27994         
27995         this.fireEvent('hide', this);
27996     },
27997     
27998     update : function()
27999     {
28000 //        var e = this.el.dom.firstChild;
28001 //        
28002 //        if(this.closable){
28003 //            e = e.nextSibling;
28004 //        }
28005 //        
28006 //        e.data = this.html || '';
28007
28008         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28009     }
28010    
28011 });
28012
28013  
28014
28015      /*
28016  * - LGPL
28017  *
28018  * Graph
28019  * 
28020  */
28021
28022
28023 /**
28024  * @class Roo.bootstrap.Graph
28025  * @extends Roo.bootstrap.Component
28026  * Bootstrap Graph class
28027 > Prameters
28028  -sm {number} sm 4
28029  -md {number} md 5
28030  @cfg {String} graphtype  bar | vbar | pie
28031  @cfg {number} g_x coodinator | centre x (pie)
28032  @cfg {number} g_y coodinator | centre y (pie)
28033  @cfg {number} g_r radius (pie)
28034  @cfg {number} g_height height of the chart (respected by all elements in the set)
28035  @cfg {number} g_width width of the chart (respected by all elements in the set)
28036  @cfg {Object} title The title of the chart
28037     
28038  -{Array}  values
28039  -opts (object) options for the chart 
28040      o {
28041      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28042      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28043      o vgutter (number)
28044      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.
28045      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28046      o to
28047      o stretch (boolean)
28048      o }
28049  -opts (object) options for the pie
28050      o{
28051      o cut
28052      o startAngle (number)
28053      o endAngle (number)
28054      } 
28055  *
28056  * @constructor
28057  * Create a new Input
28058  * @param {Object} config The config object
28059  */
28060
28061 Roo.bootstrap.Graph = function(config){
28062     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28063     
28064     this.addEvents({
28065         // img events
28066         /**
28067          * @event click
28068          * The img click event for the img.
28069          * @param {Roo.EventObject} e
28070          */
28071         "click" : true
28072     });
28073 };
28074
28075 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28076     
28077     sm: 4,
28078     md: 5,
28079     graphtype: 'bar',
28080     g_height: 250,
28081     g_width: 400,
28082     g_x: 50,
28083     g_y: 50,
28084     g_r: 30,
28085     opts:{
28086         //g_colors: this.colors,
28087         g_type: 'soft',
28088         g_gutter: '20%'
28089
28090     },
28091     title : false,
28092
28093     getAutoCreate : function(){
28094         
28095         var cfg = {
28096             tag: 'div',
28097             html : null
28098         };
28099         
28100         
28101         return  cfg;
28102     },
28103
28104     onRender : function(ct,position){
28105         
28106         
28107         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28108         
28109         if (typeof(Raphael) == 'undefined') {
28110             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28111             return;
28112         }
28113         
28114         this.raphael = Raphael(this.el.dom);
28115         
28116                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28117                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28118                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28119                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28120                 /*
28121                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28122                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28123                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28124                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28125                 
28126                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28127                 r.barchart(330, 10, 300, 220, data1);
28128                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28129                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28130                 */
28131                 
28132                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28133                 // r.barchart(30, 30, 560, 250,  xdata, {
28134                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28135                 //     axis : "0 0 1 1",
28136                 //     axisxlabels :  xdata
28137                 //     //yvalues : cols,
28138                    
28139                 // });
28140 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28141 //        
28142 //        this.load(null,xdata,{
28143 //                axis : "0 0 1 1",
28144 //                axisxlabels :  xdata
28145 //                });
28146
28147     },
28148
28149     load : function(graphtype,xdata,opts)
28150     {
28151         this.raphael.clear();
28152         if(!graphtype) {
28153             graphtype = this.graphtype;
28154         }
28155         if(!opts){
28156             opts = this.opts;
28157         }
28158         var r = this.raphael,
28159             fin = function () {
28160                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28161             },
28162             fout = function () {
28163                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28164             },
28165             pfin = function() {
28166                 this.sector.stop();
28167                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28168
28169                 if (this.label) {
28170                     this.label[0].stop();
28171                     this.label[0].attr({ r: 7.5 });
28172                     this.label[1].attr({ "font-weight": 800 });
28173                 }
28174             },
28175             pfout = function() {
28176                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28177
28178                 if (this.label) {
28179                     this.label[0].animate({ r: 5 }, 500, "bounce");
28180                     this.label[1].attr({ "font-weight": 400 });
28181                 }
28182             };
28183
28184         switch(graphtype){
28185             case 'bar':
28186                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28187                 break;
28188             case 'hbar':
28189                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28190                 break;
28191             case 'pie':
28192 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28193 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28194 //            
28195                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28196                 
28197                 break;
28198
28199         }
28200         
28201         if(this.title){
28202             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28203         }
28204         
28205     },
28206     
28207     setTitle: function(o)
28208     {
28209         this.title = o;
28210     },
28211     
28212     initEvents: function() {
28213         
28214         if(!this.href){
28215             this.el.on('click', this.onClick, this);
28216         }
28217     },
28218     
28219     onClick : function(e)
28220     {
28221         Roo.log('img onclick');
28222         this.fireEvent('click', this, e);
28223     }
28224    
28225 });
28226
28227  
28228 /*
28229  * - LGPL
28230  *
28231  * numberBox
28232  * 
28233  */
28234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28235
28236 /**
28237  * @class Roo.bootstrap.dash.NumberBox
28238  * @extends Roo.bootstrap.Component
28239  * Bootstrap NumberBox class
28240  * @cfg {String} headline Box headline
28241  * @cfg {String} content Box content
28242  * @cfg {String} icon Box icon
28243  * @cfg {String} footer Footer text
28244  * @cfg {String} fhref Footer href
28245  * 
28246  * @constructor
28247  * Create a new NumberBox
28248  * @param {Object} config The config object
28249  */
28250
28251
28252 Roo.bootstrap.dash.NumberBox = function(config){
28253     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28254     
28255 };
28256
28257 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28258     
28259     headline : '',
28260     content : '',
28261     icon : '',
28262     footer : '',
28263     fhref : '',
28264     ficon : '',
28265     
28266     getAutoCreate : function(){
28267         
28268         var cfg = {
28269             tag : 'div',
28270             cls : 'small-box ',
28271             cn : [
28272                 {
28273                     tag : 'div',
28274                     cls : 'inner',
28275                     cn :[
28276                         {
28277                             tag : 'h3',
28278                             cls : 'roo-headline',
28279                             html : this.headline
28280                         },
28281                         {
28282                             tag : 'p',
28283                             cls : 'roo-content',
28284                             html : this.content
28285                         }
28286                     ]
28287                 }
28288             ]
28289         };
28290         
28291         if(this.icon){
28292             cfg.cn.push({
28293                 tag : 'div',
28294                 cls : 'icon',
28295                 cn :[
28296                     {
28297                         tag : 'i',
28298                         cls : 'ion ' + this.icon
28299                     }
28300                 ]
28301             });
28302         }
28303         
28304         if(this.footer){
28305             var footer = {
28306                 tag : 'a',
28307                 cls : 'small-box-footer',
28308                 href : this.fhref || '#',
28309                 html : this.footer
28310             };
28311             
28312             cfg.cn.push(footer);
28313             
28314         }
28315         
28316         return  cfg;
28317     },
28318
28319     onRender : function(ct,position){
28320         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28321
28322
28323        
28324                 
28325     },
28326
28327     setHeadline: function (value)
28328     {
28329         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28330     },
28331     
28332     setFooter: function (value, href)
28333     {
28334         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28335         
28336         if(href){
28337             this.el.select('a.small-box-footer',true).first().attr('href', href);
28338         }
28339         
28340     },
28341
28342     setContent: function (value)
28343     {
28344         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28345     },
28346
28347     initEvents: function() 
28348     {   
28349         
28350     }
28351     
28352 });
28353
28354  
28355 /*
28356  * - LGPL
28357  *
28358  * TabBox
28359  * 
28360  */
28361 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28362
28363 /**
28364  * @class Roo.bootstrap.dash.TabBox
28365  * @extends Roo.bootstrap.Component
28366  * Bootstrap TabBox class
28367  * @cfg {String} title Title of the TabBox
28368  * @cfg {String} icon Icon of the TabBox
28369  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28370  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28371  * 
28372  * @constructor
28373  * Create a new TabBox
28374  * @param {Object} config The config object
28375  */
28376
28377
28378 Roo.bootstrap.dash.TabBox = function(config){
28379     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28380     this.addEvents({
28381         // raw events
28382         /**
28383          * @event addpane
28384          * When a pane is added
28385          * @param {Roo.bootstrap.dash.TabPane} pane
28386          */
28387         "addpane" : true,
28388         /**
28389          * @event activatepane
28390          * When a pane is activated
28391          * @param {Roo.bootstrap.dash.TabPane} pane
28392          */
28393         "activatepane" : true
28394         
28395          
28396     });
28397     
28398     this.panes = [];
28399 };
28400
28401 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28402
28403     title : '',
28404     icon : false,
28405     showtabs : true,
28406     tabScrollable : false,
28407     
28408     getChildContainer : function()
28409     {
28410         return this.el.select('.tab-content', true).first();
28411     },
28412     
28413     getAutoCreate : function(){
28414         
28415         var header = {
28416             tag: 'li',
28417             cls: 'pull-left header',
28418             html: this.title,
28419             cn : []
28420         };
28421         
28422         if(this.icon){
28423             header.cn.push({
28424                 tag: 'i',
28425                 cls: 'fa ' + this.icon
28426             });
28427         }
28428         
28429         var h = {
28430             tag: 'ul',
28431             cls: 'nav nav-tabs pull-right',
28432             cn: [
28433                 header
28434             ]
28435         };
28436         
28437         if(this.tabScrollable){
28438             h = {
28439                 tag: 'div',
28440                 cls: 'tab-header',
28441                 cn: [
28442                     {
28443                         tag: 'ul',
28444                         cls: 'nav nav-tabs pull-right',
28445                         cn: [
28446                             header
28447                         ]
28448                     }
28449                 ]
28450             };
28451         }
28452         
28453         var cfg = {
28454             tag: 'div',
28455             cls: 'nav-tabs-custom',
28456             cn: [
28457                 h,
28458                 {
28459                     tag: 'div',
28460                     cls: 'tab-content no-padding',
28461                     cn: []
28462                 }
28463             ]
28464         };
28465
28466         return  cfg;
28467     },
28468     initEvents : function()
28469     {
28470         //Roo.log('add add pane handler');
28471         this.on('addpane', this.onAddPane, this);
28472     },
28473      /**
28474      * Updates the box title
28475      * @param {String} html to set the title to.
28476      */
28477     setTitle : function(value)
28478     {
28479         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28480     },
28481     onAddPane : function(pane)
28482     {
28483         this.panes.push(pane);
28484         //Roo.log('addpane');
28485         //Roo.log(pane);
28486         // tabs are rendere left to right..
28487         if(!this.showtabs){
28488             return;
28489         }
28490         
28491         var ctr = this.el.select('.nav-tabs', true).first();
28492          
28493          
28494         var existing = ctr.select('.nav-tab',true);
28495         var qty = existing.getCount();;
28496         
28497         
28498         var tab = ctr.createChild({
28499             tag : 'li',
28500             cls : 'nav-tab' + (qty ? '' : ' active'),
28501             cn : [
28502                 {
28503                     tag : 'a',
28504                     href:'#',
28505                     html : pane.title
28506                 }
28507             ]
28508         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28509         pane.tab = tab;
28510         
28511         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28512         if (!qty) {
28513             pane.el.addClass('active');
28514         }
28515         
28516                 
28517     },
28518     onTabClick : function(ev,un,ob,pane)
28519     {
28520         //Roo.log('tab - prev default');
28521         ev.preventDefault();
28522         
28523         
28524         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28525         pane.tab.addClass('active');
28526         //Roo.log(pane.title);
28527         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28528         // technically we should have a deactivate event.. but maybe add later.
28529         // and it should not de-activate the selected tab...
28530         this.fireEvent('activatepane', pane);
28531         pane.el.addClass('active');
28532         pane.fireEvent('activate');
28533         
28534         
28535     },
28536     
28537     getActivePane : function()
28538     {
28539         var r = false;
28540         Roo.each(this.panes, function(p) {
28541             if(p.el.hasClass('active')){
28542                 r = p;
28543                 return false;
28544             }
28545             
28546             return;
28547         });
28548         
28549         return r;
28550     }
28551     
28552     
28553 });
28554
28555  
28556 /*
28557  * - LGPL
28558  *
28559  * Tab pane
28560  * 
28561  */
28562 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28563 /**
28564  * @class Roo.bootstrap.TabPane
28565  * @extends Roo.bootstrap.Component
28566  * Bootstrap TabPane class
28567  * @cfg {Boolean} active (false | true) Default false
28568  * @cfg {String} title title of panel
28569
28570  * 
28571  * @constructor
28572  * Create a new TabPane
28573  * @param {Object} config The config object
28574  */
28575
28576 Roo.bootstrap.dash.TabPane = function(config){
28577     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28578     
28579     this.addEvents({
28580         // raw events
28581         /**
28582          * @event activate
28583          * When a pane is activated
28584          * @param {Roo.bootstrap.dash.TabPane} pane
28585          */
28586         "activate" : true
28587          
28588     });
28589 };
28590
28591 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28592     
28593     active : false,
28594     title : '',
28595     
28596     // the tabBox that this is attached to.
28597     tab : false,
28598      
28599     getAutoCreate : function() 
28600     {
28601         var cfg = {
28602             tag: 'div',
28603             cls: 'tab-pane'
28604         };
28605         
28606         if(this.active){
28607             cfg.cls += ' active';
28608         }
28609         
28610         return cfg;
28611     },
28612     initEvents  : function()
28613     {
28614         //Roo.log('trigger add pane handler');
28615         this.parent().fireEvent('addpane', this)
28616     },
28617     
28618      /**
28619      * Updates the tab title 
28620      * @param {String} html to set the title to.
28621      */
28622     setTitle: function(str)
28623     {
28624         if (!this.tab) {
28625             return;
28626         }
28627         this.title = str;
28628         this.tab.select('a', true).first().dom.innerHTML = str;
28629         
28630     }
28631     
28632     
28633     
28634 });
28635
28636  
28637
28638
28639  /*
28640  * - LGPL
28641  *
28642  * menu
28643  * 
28644  */
28645 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28646
28647 /**
28648  * @class Roo.bootstrap.menu.Menu
28649  * @extends Roo.bootstrap.Component
28650  * Bootstrap Menu class - container for Menu
28651  * @cfg {String} html Text of the menu
28652  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28653  * @cfg {String} icon Font awesome icon
28654  * @cfg {String} pos Menu align to (top | bottom) default bottom
28655  * 
28656  * 
28657  * @constructor
28658  * Create a new Menu
28659  * @param {Object} config The config object
28660  */
28661
28662
28663 Roo.bootstrap.menu.Menu = function(config){
28664     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28665     
28666     this.addEvents({
28667         /**
28668          * @event beforeshow
28669          * Fires before this menu is displayed
28670          * @param {Roo.bootstrap.menu.Menu} this
28671          */
28672         beforeshow : true,
28673         /**
28674          * @event beforehide
28675          * Fires before this menu is hidden
28676          * @param {Roo.bootstrap.menu.Menu} this
28677          */
28678         beforehide : true,
28679         /**
28680          * @event show
28681          * Fires after this menu is displayed
28682          * @param {Roo.bootstrap.menu.Menu} this
28683          */
28684         show : true,
28685         /**
28686          * @event hide
28687          * Fires after this menu is hidden
28688          * @param {Roo.bootstrap.menu.Menu} this
28689          */
28690         hide : true,
28691         /**
28692          * @event click
28693          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28694          * @param {Roo.bootstrap.menu.Menu} this
28695          * @param {Roo.EventObject} e
28696          */
28697         click : true
28698     });
28699     
28700 };
28701
28702 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28703     
28704     submenu : false,
28705     html : '',
28706     weight : 'default',
28707     icon : false,
28708     pos : 'bottom',
28709     
28710     
28711     getChildContainer : function() {
28712         if(this.isSubMenu){
28713             return this.el;
28714         }
28715         
28716         return this.el.select('ul.dropdown-menu', true).first();  
28717     },
28718     
28719     getAutoCreate : function()
28720     {
28721         var text = [
28722             {
28723                 tag : 'span',
28724                 cls : 'roo-menu-text',
28725                 html : this.html
28726             }
28727         ];
28728         
28729         if(this.icon){
28730             text.unshift({
28731                 tag : 'i',
28732                 cls : 'fa ' + this.icon
28733             })
28734         }
28735         
28736         
28737         var cfg = {
28738             tag : 'div',
28739             cls : 'btn-group',
28740             cn : [
28741                 {
28742                     tag : 'button',
28743                     cls : 'dropdown-button btn btn-' + this.weight,
28744                     cn : text
28745                 },
28746                 {
28747                     tag : 'button',
28748                     cls : 'dropdown-toggle btn btn-' + this.weight,
28749                     cn : [
28750                         {
28751                             tag : 'span',
28752                             cls : 'caret'
28753                         }
28754                     ]
28755                 },
28756                 {
28757                     tag : 'ul',
28758                     cls : 'dropdown-menu'
28759                 }
28760             ]
28761             
28762         };
28763         
28764         if(this.pos == 'top'){
28765             cfg.cls += ' dropup';
28766         }
28767         
28768         if(this.isSubMenu){
28769             cfg = {
28770                 tag : 'ul',
28771                 cls : 'dropdown-menu'
28772             }
28773         }
28774         
28775         return cfg;
28776     },
28777     
28778     onRender : function(ct, position)
28779     {
28780         this.isSubMenu = ct.hasClass('dropdown-submenu');
28781         
28782         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28783     },
28784     
28785     initEvents : function() 
28786     {
28787         if(this.isSubMenu){
28788             return;
28789         }
28790         
28791         this.hidden = true;
28792         
28793         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28794         this.triggerEl.on('click', this.onTriggerPress, this);
28795         
28796         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28797         this.buttonEl.on('click', this.onClick, this);
28798         
28799     },
28800     
28801     list : function()
28802     {
28803         if(this.isSubMenu){
28804             return this.el;
28805         }
28806         
28807         return this.el.select('ul.dropdown-menu', true).first();
28808     },
28809     
28810     onClick : function(e)
28811     {
28812         this.fireEvent("click", this, e);
28813     },
28814     
28815     onTriggerPress  : function(e)
28816     {   
28817         if (this.isVisible()) {
28818             this.hide();
28819         } else {
28820             this.show();
28821         }
28822     },
28823     
28824     isVisible : function(){
28825         return !this.hidden;
28826     },
28827     
28828     show : function()
28829     {
28830         this.fireEvent("beforeshow", this);
28831         
28832         this.hidden = false;
28833         this.el.addClass('open');
28834         
28835         Roo.get(document).on("mouseup", this.onMouseUp, this);
28836         
28837         this.fireEvent("show", this);
28838         
28839         
28840     },
28841     
28842     hide : function()
28843     {
28844         this.fireEvent("beforehide", this);
28845         
28846         this.hidden = true;
28847         this.el.removeClass('open');
28848         
28849         Roo.get(document).un("mouseup", this.onMouseUp);
28850         
28851         this.fireEvent("hide", this);
28852     },
28853     
28854     onMouseUp : function()
28855     {
28856         this.hide();
28857     }
28858     
28859 });
28860
28861  
28862  /*
28863  * - LGPL
28864  *
28865  * menu item
28866  * 
28867  */
28868 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28869
28870 /**
28871  * @class Roo.bootstrap.menu.Item
28872  * @extends Roo.bootstrap.Component
28873  * Bootstrap MenuItem class
28874  * @cfg {Boolean} submenu (true | false) default false
28875  * @cfg {String} html text of the item
28876  * @cfg {String} href the link
28877  * @cfg {Boolean} disable (true | false) default false
28878  * @cfg {Boolean} preventDefault (true | false) default true
28879  * @cfg {String} icon Font awesome icon
28880  * @cfg {String} pos Submenu align to (left | right) default right 
28881  * 
28882  * 
28883  * @constructor
28884  * Create a new Item
28885  * @param {Object} config The config object
28886  */
28887
28888
28889 Roo.bootstrap.menu.Item = function(config){
28890     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28891     this.addEvents({
28892         /**
28893          * @event mouseover
28894          * Fires when the mouse is hovering over this menu
28895          * @param {Roo.bootstrap.menu.Item} this
28896          * @param {Roo.EventObject} e
28897          */
28898         mouseover : true,
28899         /**
28900          * @event mouseout
28901          * Fires when the mouse exits this menu
28902          * @param {Roo.bootstrap.menu.Item} this
28903          * @param {Roo.EventObject} e
28904          */
28905         mouseout : true,
28906         // raw events
28907         /**
28908          * @event click
28909          * The raw click event for the entire grid.
28910          * @param {Roo.EventObject} e
28911          */
28912         click : true
28913     });
28914 };
28915
28916 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28917     
28918     submenu : false,
28919     href : '',
28920     html : '',
28921     preventDefault: true,
28922     disable : false,
28923     icon : false,
28924     pos : 'right',
28925     
28926     getAutoCreate : function()
28927     {
28928         var text = [
28929             {
28930                 tag : 'span',
28931                 cls : 'roo-menu-item-text',
28932                 html : this.html
28933             }
28934         ];
28935         
28936         if(this.icon){
28937             text.unshift({
28938                 tag : 'i',
28939                 cls : 'fa ' + this.icon
28940             })
28941         }
28942         
28943         var cfg = {
28944             tag : 'li',
28945             cn : [
28946                 {
28947                     tag : 'a',
28948                     href : this.href || '#',
28949                     cn : text
28950                 }
28951             ]
28952         };
28953         
28954         if(this.disable){
28955             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28956         }
28957         
28958         if(this.submenu){
28959             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28960             
28961             if(this.pos == 'left'){
28962                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28963             }
28964         }
28965         
28966         return cfg;
28967     },
28968     
28969     initEvents : function() 
28970     {
28971         this.el.on('mouseover', this.onMouseOver, this);
28972         this.el.on('mouseout', this.onMouseOut, this);
28973         
28974         this.el.select('a', true).first().on('click', this.onClick, this);
28975         
28976     },
28977     
28978     onClick : function(e)
28979     {
28980         if(this.preventDefault){
28981             e.preventDefault();
28982         }
28983         
28984         this.fireEvent("click", this, e);
28985     },
28986     
28987     onMouseOver : function(e)
28988     {
28989         if(this.submenu && this.pos == 'left'){
28990             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28991         }
28992         
28993         this.fireEvent("mouseover", this, e);
28994     },
28995     
28996     onMouseOut : function(e)
28997     {
28998         this.fireEvent("mouseout", this, e);
28999     }
29000 });
29001
29002  
29003
29004  /*
29005  * - LGPL
29006  *
29007  * menu separator
29008  * 
29009  */
29010 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29011
29012 /**
29013  * @class Roo.bootstrap.menu.Separator
29014  * @extends Roo.bootstrap.Component
29015  * Bootstrap Separator class
29016  * 
29017  * @constructor
29018  * Create a new Separator
29019  * @param {Object} config The config object
29020  */
29021
29022
29023 Roo.bootstrap.menu.Separator = function(config){
29024     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29025 };
29026
29027 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29028     
29029     getAutoCreate : function(){
29030         var cfg = {
29031             tag : 'li',
29032             cls: 'dropdown-divider divider'
29033         };
29034         
29035         return cfg;
29036     }
29037    
29038 });
29039
29040  
29041
29042  /*
29043  * - LGPL
29044  *
29045  * Tooltip
29046  * 
29047  */
29048
29049 /**
29050  * @class Roo.bootstrap.Tooltip
29051  * Bootstrap Tooltip class
29052  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29053  * to determine which dom element triggers the tooltip.
29054  * 
29055  * It needs to add support for additional attributes like tooltip-position
29056  * 
29057  * @constructor
29058  * Create a new Toolti
29059  * @param {Object} config The config object
29060  */
29061
29062 Roo.bootstrap.Tooltip = function(config){
29063     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29064     
29065     this.alignment = Roo.bootstrap.Tooltip.alignment;
29066     
29067     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29068         this.alignment = config.alignment;
29069     }
29070     
29071 };
29072
29073 Roo.apply(Roo.bootstrap.Tooltip, {
29074     /**
29075      * @function init initialize tooltip monitoring.
29076      * @static
29077      */
29078     currentEl : false,
29079     currentTip : false,
29080     currentRegion : false,
29081     
29082     //  init : delay?
29083     
29084     init : function()
29085     {
29086         Roo.get(document).on('mouseover', this.enter ,this);
29087         Roo.get(document).on('mouseout', this.leave, this);
29088          
29089         
29090         this.currentTip = new Roo.bootstrap.Tooltip();
29091     },
29092     
29093     enter : function(ev)
29094     {
29095         var dom = ev.getTarget();
29096         
29097         //Roo.log(['enter',dom]);
29098         var el = Roo.fly(dom);
29099         if (this.currentEl) {
29100             //Roo.log(dom);
29101             //Roo.log(this.currentEl);
29102             //Roo.log(this.currentEl.contains(dom));
29103             if (this.currentEl == el) {
29104                 return;
29105             }
29106             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29107                 return;
29108             }
29109
29110         }
29111         
29112         if (this.currentTip.el) {
29113             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29114         }    
29115         //Roo.log(ev);
29116         
29117         if(!el || el.dom == document){
29118             return;
29119         }
29120         
29121         var bindEl = el; 
29122         var pel = false;
29123         if (!el.attr('tooltip')) {
29124             pel = el.findParent("[tooltip]");
29125             if (pel) {
29126                 bindEl = Roo.get(pel);
29127             }
29128         }
29129         
29130        
29131         
29132         // you can not look for children, as if el is the body.. then everythign is the child..
29133         if (!pel && !el.attr('tooltip')) { //
29134             if (!el.select("[tooltip]").elements.length) {
29135                 return;
29136             }
29137             // is the mouse over this child...?
29138             bindEl = el.select("[tooltip]").first();
29139             var xy = ev.getXY();
29140             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29141                 //Roo.log("not in region.");
29142                 return;
29143             }
29144             //Roo.log("child element over..");
29145             
29146         }
29147         this.currentEl = el;
29148         this.currentTip.bind(bindEl);
29149         this.currentRegion = Roo.lib.Region.getRegion(dom);
29150         this.currentTip.enter();
29151         
29152     },
29153     leave : function(ev)
29154     {
29155         var dom = ev.getTarget();
29156         //Roo.log(['leave',dom]);
29157         if (!this.currentEl) {
29158             return;
29159         }
29160         
29161         
29162         if (dom != this.currentEl.dom) {
29163             return;
29164         }
29165         var xy = ev.getXY();
29166         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29167             return;
29168         }
29169         // only activate leave if mouse cursor is outside... bounding box..
29170         
29171         
29172         
29173         
29174         if (this.currentTip) {
29175             this.currentTip.leave();
29176         }
29177         //Roo.log('clear currentEl');
29178         this.currentEl = false;
29179         
29180         
29181     },
29182     alignment : {
29183         'left' : ['r-l', [-2,0], 'right'],
29184         'right' : ['l-r', [2,0], 'left'],
29185         'bottom' : ['t-b', [0,2], 'top'],
29186         'top' : [ 'b-t', [0,-2], 'bottom']
29187     }
29188     
29189 });
29190
29191
29192 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29193     
29194     
29195     bindEl : false,
29196     
29197     delay : null, // can be { show : 300 , hide: 500}
29198     
29199     timeout : null,
29200     
29201     hoverState : null, //???
29202     
29203     placement : 'bottom', 
29204     
29205     alignment : false,
29206     
29207     getAutoCreate : function(){
29208     
29209         var cfg = {
29210            cls : 'tooltip',   
29211            role : 'tooltip',
29212            cn : [
29213                 {
29214                     cls : 'tooltip-arrow arrow'
29215                 },
29216                 {
29217                     cls : 'tooltip-inner'
29218                 }
29219            ]
29220         };
29221         
29222         return cfg;
29223     },
29224     bind : function(el)
29225     {
29226         this.bindEl = el;
29227     },
29228     
29229     initEvents : function()
29230     {
29231         this.arrowEl = this.el.select('.arrow', true).first();
29232         this.innerEl = this.el.select('.tooltip-inner', true).first();
29233     },
29234     
29235     enter : function () {
29236        
29237         if (this.timeout != null) {
29238             clearTimeout(this.timeout);
29239         }
29240         
29241         this.hoverState = 'in';
29242          //Roo.log("enter - show");
29243         if (!this.delay || !this.delay.show) {
29244             this.show();
29245             return;
29246         }
29247         var _t = this;
29248         this.timeout = setTimeout(function () {
29249             if (_t.hoverState == 'in') {
29250                 _t.show();
29251             }
29252         }, this.delay.show);
29253     },
29254     leave : function()
29255     {
29256         clearTimeout(this.timeout);
29257     
29258         this.hoverState = 'out';
29259          if (!this.delay || !this.delay.hide) {
29260             this.hide();
29261             return;
29262         }
29263        
29264         var _t = this;
29265         this.timeout = setTimeout(function () {
29266             //Roo.log("leave - timeout");
29267             
29268             if (_t.hoverState == 'out') {
29269                 _t.hide();
29270                 Roo.bootstrap.Tooltip.currentEl = false;
29271             }
29272         }, delay);
29273     },
29274     
29275     show : function (msg)
29276     {
29277         if (!this.el) {
29278             this.render(document.body);
29279         }
29280         // set content.
29281         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29282         
29283         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29284         
29285         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29286         
29287         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29288                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29289         
29290         var placement = typeof this.placement == 'function' ?
29291             this.placement.call(this, this.el, on_el) :
29292             this.placement;
29293             
29294         var autoToken = /\s?auto?\s?/i;
29295         var autoPlace = autoToken.test(placement);
29296         if (autoPlace) {
29297             placement = placement.replace(autoToken, '') || 'top';
29298         }
29299         
29300         //this.el.detach()
29301         //this.el.setXY([0,0]);
29302         this.el.show();
29303         //this.el.dom.style.display='block';
29304         
29305         //this.el.appendTo(on_el);
29306         
29307         var p = this.getPosition();
29308         var box = this.el.getBox();
29309         
29310         if (autoPlace) {
29311             // fixme..
29312         }
29313         
29314         var align = this.alignment[placement];
29315         
29316         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29317         
29318         if(placement == 'top' || placement == 'bottom'){
29319             if(xy[0] < 0){
29320                 placement = 'right';
29321             }
29322             
29323             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29324                 placement = 'left';
29325             }
29326             
29327             var scroll = Roo.select('body', true).first().getScroll();
29328             
29329             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29330                 placement = 'top';
29331             }
29332             
29333             align = this.alignment[placement];
29334             
29335             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29336             
29337         }
29338         
29339         var elems = document.getElementsByTagName('div');
29340         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29341         for (var i = 0; i < elems.length; i++) {
29342           var zindex = Number.parseInt(
29343                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29344                 10
29345           );
29346           if (zindex > highest) {
29347             highest = zindex;
29348           }
29349         }
29350         
29351         
29352         
29353         this.el.dom.style.zIndex = highest;
29354         
29355         this.el.alignTo(this.bindEl, align[0],align[1]);
29356         //var arrow = this.el.select('.arrow',true).first();
29357         //arrow.set(align[2], 
29358         
29359         this.el.addClass(placement);
29360         this.el.addClass("bs-tooltip-"+ placement);
29361         
29362         this.el.addClass('in fade show');
29363         
29364         this.hoverState = null;
29365         
29366         if (this.el.hasClass('fade')) {
29367             // fade it?
29368         }
29369         
29370         
29371         
29372         
29373         
29374     },
29375     hide : function()
29376     {
29377          
29378         if (!this.el) {
29379             return;
29380         }
29381         //this.el.setXY([0,0]);
29382         this.el.removeClass(['show', 'in']);
29383         //this.el.hide();
29384         
29385     }
29386     
29387 });
29388  
29389
29390  /*
29391  * - LGPL
29392  *
29393  * Location Picker
29394  * 
29395  */
29396
29397 /**
29398  * @class Roo.bootstrap.LocationPicker
29399  * @extends Roo.bootstrap.Component
29400  * Bootstrap LocationPicker class
29401  * @cfg {Number} latitude Position when init default 0
29402  * @cfg {Number} longitude Position when init default 0
29403  * @cfg {Number} zoom default 15
29404  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29405  * @cfg {Boolean} mapTypeControl default false
29406  * @cfg {Boolean} disableDoubleClickZoom default false
29407  * @cfg {Boolean} scrollwheel default true
29408  * @cfg {Boolean} streetViewControl default false
29409  * @cfg {Number} radius default 0
29410  * @cfg {String} locationName
29411  * @cfg {Boolean} draggable default true
29412  * @cfg {Boolean} enableAutocomplete default false
29413  * @cfg {Boolean} enableReverseGeocode default true
29414  * @cfg {String} markerTitle
29415  * 
29416  * @constructor
29417  * Create a new LocationPicker
29418  * @param {Object} config The config object
29419  */
29420
29421
29422 Roo.bootstrap.LocationPicker = function(config){
29423     
29424     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29425     
29426     this.addEvents({
29427         /**
29428          * @event initial
29429          * Fires when the picker initialized.
29430          * @param {Roo.bootstrap.LocationPicker} this
29431          * @param {Google Location} location
29432          */
29433         initial : true,
29434         /**
29435          * @event positionchanged
29436          * Fires when the picker position changed.
29437          * @param {Roo.bootstrap.LocationPicker} this
29438          * @param {Google Location} location
29439          */
29440         positionchanged : true,
29441         /**
29442          * @event resize
29443          * Fires when the map resize.
29444          * @param {Roo.bootstrap.LocationPicker} this
29445          */
29446         resize : true,
29447         /**
29448          * @event show
29449          * Fires when the map show.
29450          * @param {Roo.bootstrap.LocationPicker} this
29451          */
29452         show : true,
29453         /**
29454          * @event hide
29455          * Fires when the map hide.
29456          * @param {Roo.bootstrap.LocationPicker} this
29457          */
29458         hide : true,
29459         /**
29460          * @event mapClick
29461          * Fires when click the map.
29462          * @param {Roo.bootstrap.LocationPicker} this
29463          * @param {Map event} e
29464          */
29465         mapClick : true,
29466         /**
29467          * @event mapRightClick
29468          * Fires when right click the map.
29469          * @param {Roo.bootstrap.LocationPicker} this
29470          * @param {Map event} e
29471          */
29472         mapRightClick : true,
29473         /**
29474          * @event markerClick
29475          * Fires when click the marker.
29476          * @param {Roo.bootstrap.LocationPicker} this
29477          * @param {Map event} e
29478          */
29479         markerClick : true,
29480         /**
29481          * @event markerRightClick
29482          * Fires when right click the marker.
29483          * @param {Roo.bootstrap.LocationPicker} this
29484          * @param {Map event} e
29485          */
29486         markerRightClick : true,
29487         /**
29488          * @event OverlayViewDraw
29489          * Fires when OverlayView Draw
29490          * @param {Roo.bootstrap.LocationPicker} this
29491          */
29492         OverlayViewDraw : true,
29493         /**
29494          * @event OverlayViewOnAdd
29495          * Fires when OverlayView Draw
29496          * @param {Roo.bootstrap.LocationPicker} this
29497          */
29498         OverlayViewOnAdd : true,
29499         /**
29500          * @event OverlayViewOnRemove
29501          * Fires when OverlayView Draw
29502          * @param {Roo.bootstrap.LocationPicker} this
29503          */
29504         OverlayViewOnRemove : true,
29505         /**
29506          * @event OverlayViewShow
29507          * Fires when OverlayView Draw
29508          * @param {Roo.bootstrap.LocationPicker} this
29509          * @param {Pixel} cpx
29510          */
29511         OverlayViewShow : true,
29512         /**
29513          * @event OverlayViewHide
29514          * Fires when OverlayView Draw
29515          * @param {Roo.bootstrap.LocationPicker} this
29516          */
29517         OverlayViewHide : true,
29518         /**
29519          * @event loadexception
29520          * Fires when load google lib failed.
29521          * @param {Roo.bootstrap.LocationPicker} this
29522          */
29523         loadexception : true
29524     });
29525         
29526 };
29527
29528 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29529     
29530     gMapContext: false,
29531     
29532     latitude: 0,
29533     longitude: 0,
29534     zoom: 15,
29535     mapTypeId: false,
29536     mapTypeControl: false,
29537     disableDoubleClickZoom: false,
29538     scrollwheel: true,
29539     streetViewControl: false,
29540     radius: 0,
29541     locationName: '',
29542     draggable: true,
29543     enableAutocomplete: false,
29544     enableReverseGeocode: true,
29545     markerTitle: '',
29546     
29547     getAutoCreate: function()
29548     {
29549
29550         var cfg = {
29551             tag: 'div',
29552             cls: 'roo-location-picker'
29553         };
29554         
29555         return cfg
29556     },
29557     
29558     initEvents: function(ct, position)
29559     {       
29560         if(!this.el.getWidth() || this.isApplied()){
29561             return;
29562         }
29563         
29564         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29565         
29566         this.initial();
29567     },
29568     
29569     initial: function()
29570     {
29571         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29572             this.fireEvent('loadexception', this);
29573             return;
29574         }
29575         
29576         if(!this.mapTypeId){
29577             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29578         }
29579         
29580         this.gMapContext = this.GMapContext();
29581         
29582         this.initOverlayView();
29583         
29584         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29585         
29586         var _this = this;
29587                 
29588         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29589             _this.setPosition(_this.gMapContext.marker.position);
29590         });
29591         
29592         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29593             _this.fireEvent('mapClick', this, event);
29594             
29595         });
29596
29597         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29598             _this.fireEvent('mapRightClick', this, event);
29599             
29600         });
29601         
29602         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29603             _this.fireEvent('markerClick', this, event);
29604             
29605         });
29606
29607         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29608             _this.fireEvent('markerRightClick', this, event);
29609             
29610         });
29611         
29612         this.setPosition(this.gMapContext.location);
29613         
29614         this.fireEvent('initial', this, this.gMapContext.location);
29615     },
29616     
29617     initOverlayView: function()
29618     {
29619         var _this = this;
29620         
29621         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29622             
29623             draw: function()
29624             {
29625                 _this.fireEvent('OverlayViewDraw', _this);
29626             },
29627             
29628             onAdd: function()
29629             {
29630                 _this.fireEvent('OverlayViewOnAdd', _this);
29631             },
29632             
29633             onRemove: function()
29634             {
29635                 _this.fireEvent('OverlayViewOnRemove', _this);
29636             },
29637             
29638             show: function(cpx)
29639             {
29640                 _this.fireEvent('OverlayViewShow', _this, cpx);
29641             },
29642             
29643             hide: function()
29644             {
29645                 _this.fireEvent('OverlayViewHide', _this);
29646             }
29647             
29648         });
29649     },
29650     
29651     fromLatLngToContainerPixel: function(event)
29652     {
29653         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29654     },
29655     
29656     isApplied: function() 
29657     {
29658         return this.getGmapContext() == false ? false : true;
29659     },
29660     
29661     getGmapContext: function() 
29662     {
29663         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29664     },
29665     
29666     GMapContext: function() 
29667     {
29668         var position = new google.maps.LatLng(this.latitude, this.longitude);
29669         
29670         var _map = new google.maps.Map(this.el.dom, {
29671             center: position,
29672             zoom: this.zoom,
29673             mapTypeId: this.mapTypeId,
29674             mapTypeControl: this.mapTypeControl,
29675             disableDoubleClickZoom: this.disableDoubleClickZoom,
29676             scrollwheel: this.scrollwheel,
29677             streetViewControl: this.streetViewControl,
29678             locationName: this.locationName,
29679             draggable: this.draggable,
29680             enableAutocomplete: this.enableAutocomplete,
29681             enableReverseGeocode: this.enableReverseGeocode
29682         });
29683         
29684         var _marker = new google.maps.Marker({
29685             position: position,
29686             map: _map,
29687             title: this.markerTitle,
29688             draggable: this.draggable
29689         });
29690         
29691         return {
29692             map: _map,
29693             marker: _marker,
29694             circle: null,
29695             location: position,
29696             radius: this.radius,
29697             locationName: this.locationName,
29698             addressComponents: {
29699                 formatted_address: null,
29700                 addressLine1: null,
29701                 addressLine2: null,
29702                 streetName: null,
29703                 streetNumber: null,
29704                 city: null,
29705                 district: null,
29706                 state: null,
29707                 stateOrProvince: null
29708             },
29709             settings: this,
29710             domContainer: this.el.dom,
29711             geodecoder: new google.maps.Geocoder()
29712         };
29713     },
29714     
29715     drawCircle: function(center, radius, options) 
29716     {
29717         if (this.gMapContext.circle != null) {
29718             this.gMapContext.circle.setMap(null);
29719         }
29720         if (radius > 0) {
29721             radius *= 1;
29722             options = Roo.apply({}, options, {
29723                 strokeColor: "#0000FF",
29724                 strokeOpacity: .35,
29725                 strokeWeight: 2,
29726                 fillColor: "#0000FF",
29727                 fillOpacity: .2
29728             });
29729             
29730             options.map = this.gMapContext.map;
29731             options.radius = radius;
29732             options.center = center;
29733             this.gMapContext.circle = new google.maps.Circle(options);
29734             return this.gMapContext.circle;
29735         }
29736         
29737         return null;
29738     },
29739     
29740     setPosition: function(location) 
29741     {
29742         this.gMapContext.location = location;
29743         this.gMapContext.marker.setPosition(location);
29744         this.gMapContext.map.panTo(location);
29745         this.drawCircle(location, this.gMapContext.radius, {});
29746         
29747         var _this = this;
29748         
29749         if (this.gMapContext.settings.enableReverseGeocode) {
29750             this.gMapContext.geodecoder.geocode({
29751                 latLng: this.gMapContext.location
29752             }, function(results, status) {
29753                 
29754                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29755                     _this.gMapContext.locationName = results[0].formatted_address;
29756                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29757                     
29758                     _this.fireEvent('positionchanged', this, location);
29759                 }
29760             });
29761             
29762             return;
29763         }
29764         
29765         this.fireEvent('positionchanged', this, location);
29766     },
29767     
29768     resize: function()
29769     {
29770         google.maps.event.trigger(this.gMapContext.map, "resize");
29771         
29772         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29773         
29774         this.fireEvent('resize', this);
29775     },
29776     
29777     setPositionByLatLng: function(latitude, longitude)
29778     {
29779         this.setPosition(new google.maps.LatLng(latitude, longitude));
29780     },
29781     
29782     getCurrentPosition: function() 
29783     {
29784         return {
29785             latitude: this.gMapContext.location.lat(),
29786             longitude: this.gMapContext.location.lng()
29787         };
29788     },
29789     
29790     getAddressName: function() 
29791     {
29792         return this.gMapContext.locationName;
29793     },
29794     
29795     getAddressComponents: function() 
29796     {
29797         return this.gMapContext.addressComponents;
29798     },
29799     
29800     address_component_from_google_geocode: function(address_components) 
29801     {
29802         var result = {};
29803         
29804         for (var i = 0; i < address_components.length; i++) {
29805             var component = address_components[i];
29806             if (component.types.indexOf("postal_code") >= 0) {
29807                 result.postalCode = component.short_name;
29808             } else if (component.types.indexOf("street_number") >= 0) {
29809                 result.streetNumber = component.short_name;
29810             } else if (component.types.indexOf("route") >= 0) {
29811                 result.streetName = component.short_name;
29812             } else if (component.types.indexOf("neighborhood") >= 0) {
29813                 result.city = component.short_name;
29814             } else if (component.types.indexOf("locality") >= 0) {
29815                 result.city = component.short_name;
29816             } else if (component.types.indexOf("sublocality") >= 0) {
29817                 result.district = component.short_name;
29818             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29819                 result.stateOrProvince = component.short_name;
29820             } else if (component.types.indexOf("country") >= 0) {
29821                 result.country = component.short_name;
29822             }
29823         }
29824         
29825         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29826         result.addressLine2 = "";
29827         return result;
29828     },
29829     
29830     setZoomLevel: function(zoom)
29831     {
29832         this.gMapContext.map.setZoom(zoom);
29833     },
29834     
29835     show: function()
29836     {
29837         if(!this.el){
29838             return;
29839         }
29840         
29841         this.el.show();
29842         
29843         this.resize();
29844         
29845         this.fireEvent('show', this);
29846     },
29847     
29848     hide: function()
29849     {
29850         if(!this.el){
29851             return;
29852         }
29853         
29854         this.el.hide();
29855         
29856         this.fireEvent('hide', this);
29857     }
29858     
29859 });
29860
29861 Roo.apply(Roo.bootstrap.LocationPicker, {
29862     
29863     OverlayView : function(map, options)
29864     {
29865         options = options || {};
29866         
29867         this.setMap(map);
29868     }
29869     
29870     
29871 });/**
29872  * @class Roo.bootstrap.Alert
29873  * @extends Roo.bootstrap.Component
29874  * Bootstrap Alert class - shows an alert area box
29875  * eg
29876  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29877   Enter a valid email address
29878 </div>
29879  * @licence LGPL
29880  * @cfg {String} title The title of alert
29881  * @cfg {String} html The content of alert
29882  * @cfg {String} weight (  success | info | warning | danger )
29883  * @cfg {String} fa font-awesomeicon
29884  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29885  * @cfg {Boolean} close true to show a x closer
29886  * 
29887  * 
29888  * @constructor
29889  * Create a new alert
29890  * @param {Object} config The config object
29891  */
29892
29893
29894 Roo.bootstrap.Alert = function(config){
29895     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29896     
29897 };
29898
29899 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29900     
29901     title: '',
29902     html: '',
29903     weight: false,
29904     fa: false,
29905     faicon: false, // BC
29906     close : false,
29907     
29908     
29909     getAutoCreate : function()
29910     {
29911         
29912         var cfg = {
29913             tag : 'div',
29914             cls : 'alert',
29915             cn : [
29916                 {
29917                     tag: 'button',
29918                     type :  "button",
29919                     cls: "close",
29920                     html : '×',
29921                     style : this.close ? '' : 'display:none'
29922                 },
29923                 {
29924                     tag : 'i',
29925                     cls : 'roo-alert-icon'
29926                     
29927                 },
29928                 {
29929                     tag : 'b',
29930                     cls : 'roo-alert-title',
29931                     html : this.title
29932                 },
29933                 {
29934                     tag : 'span',
29935                     cls : 'roo-alert-text',
29936                     html : this.html
29937                 }
29938             ]
29939         };
29940         
29941         if(this.faicon){
29942             cfg.cn[0].cls += ' fa ' + this.faicon;
29943         }
29944         if(this.fa){
29945             cfg.cn[0].cls += ' fa ' + this.fa;
29946         }
29947         
29948         if(this.weight){
29949             cfg.cls += ' alert-' + this.weight;
29950         }
29951         
29952         return cfg;
29953     },
29954     
29955     initEvents: function() 
29956     {
29957         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29958         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29959         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29960         if (this.seconds > 0) {
29961             this.hide.defer(this.seconds, this);
29962         }
29963     },
29964     
29965     setTitle : function(str)
29966     {
29967         this.titleEl.dom.innerHTML = str;
29968     },
29969     
29970     setText : function(str)
29971     {
29972         this.titleEl.dom.innerHTML = str;
29973     },
29974     
29975     setWeight : function(weight)
29976     {
29977         if(this.weight){
29978             this.el.removeClass('alert-' + this.weight);
29979         }
29980         
29981         this.weight = weight;
29982         
29983         this.el.addClass('alert-' + this.weight);
29984     },
29985     
29986     setIcon : function(icon)
29987     {
29988         if(this.faicon){
29989             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29990         }
29991         
29992         this.faicon = icon;
29993         
29994         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29995     },
29996     
29997     hide: function() 
29998     {
29999         this.el.hide();   
30000     },
30001     
30002     show: function() 
30003     {  
30004         this.el.show();   
30005     }
30006     
30007 });
30008
30009  
30010 /*
30011 * Licence: LGPL
30012 */
30013
30014 /**
30015  * @class Roo.bootstrap.UploadCropbox
30016  * @extends Roo.bootstrap.Component
30017  * Bootstrap UploadCropbox class
30018  * @cfg {String} emptyText show when image has been loaded
30019  * @cfg {String} rotateNotify show when image too small to rotate
30020  * @cfg {Number} errorTimeout default 3000
30021  * @cfg {Number} minWidth default 300
30022  * @cfg {Number} minHeight default 300
30023  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30024  * @cfg {Boolean} isDocument (true|false) default false
30025  * @cfg {String} url action url
30026  * @cfg {String} paramName default 'imageUpload'
30027  * @cfg {String} method default POST
30028  * @cfg {Boolean} loadMask (true|false) default true
30029  * @cfg {Boolean} loadingText default 'Loading...'
30030  * 
30031  * @constructor
30032  * Create a new UploadCropbox
30033  * @param {Object} config The config object
30034  */
30035
30036 Roo.bootstrap.UploadCropbox = function(config){
30037     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30038     
30039     this.addEvents({
30040         /**
30041          * @event beforeselectfile
30042          * Fire before select file
30043          * @param {Roo.bootstrap.UploadCropbox} this
30044          */
30045         "beforeselectfile" : true,
30046         /**
30047          * @event initial
30048          * Fire after initEvent
30049          * @param {Roo.bootstrap.UploadCropbox} this
30050          */
30051         "initial" : true,
30052         /**
30053          * @event crop
30054          * Fire after initEvent
30055          * @param {Roo.bootstrap.UploadCropbox} this
30056          * @param {String} data
30057          */
30058         "crop" : true,
30059         /**
30060          * @event prepare
30061          * Fire when preparing the file data
30062          * @param {Roo.bootstrap.UploadCropbox} this
30063          * @param {Object} file
30064          */
30065         "prepare" : true,
30066         /**
30067          * @event exception
30068          * Fire when get exception
30069          * @param {Roo.bootstrap.UploadCropbox} this
30070          * @param {XMLHttpRequest} xhr
30071          */
30072         "exception" : true,
30073         /**
30074          * @event beforeloadcanvas
30075          * Fire before load the canvas
30076          * @param {Roo.bootstrap.UploadCropbox} this
30077          * @param {String} src
30078          */
30079         "beforeloadcanvas" : true,
30080         /**
30081          * @event trash
30082          * Fire when trash image
30083          * @param {Roo.bootstrap.UploadCropbox} this
30084          */
30085         "trash" : true,
30086         /**
30087          * @event download
30088          * Fire when download the image
30089          * @param {Roo.bootstrap.UploadCropbox} this
30090          */
30091         "download" : true,
30092         /**
30093          * @event footerbuttonclick
30094          * Fire when footerbuttonclick
30095          * @param {Roo.bootstrap.UploadCropbox} this
30096          * @param {String} type
30097          */
30098         "footerbuttonclick" : true,
30099         /**
30100          * @event resize
30101          * Fire when resize
30102          * @param {Roo.bootstrap.UploadCropbox} this
30103          */
30104         "resize" : true,
30105         /**
30106          * @event rotate
30107          * Fire when rotate the image
30108          * @param {Roo.bootstrap.UploadCropbox} this
30109          * @param {String} pos
30110          */
30111         "rotate" : true,
30112         /**
30113          * @event inspect
30114          * Fire when inspect the file
30115          * @param {Roo.bootstrap.UploadCropbox} this
30116          * @param {Object} file
30117          */
30118         "inspect" : true,
30119         /**
30120          * @event upload
30121          * Fire when xhr upload the file
30122          * @param {Roo.bootstrap.UploadCropbox} this
30123          * @param {Object} data
30124          */
30125         "upload" : true,
30126         /**
30127          * @event arrange
30128          * Fire when arrange the file data
30129          * @param {Roo.bootstrap.UploadCropbox} this
30130          * @param {Object} formData
30131          */
30132         "arrange" : true
30133     });
30134     
30135     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30136 };
30137
30138 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30139     
30140     emptyText : 'Click to upload image',
30141     rotateNotify : 'Image is too small to rotate',
30142     errorTimeout : 3000,
30143     scale : 0,
30144     baseScale : 1,
30145     rotate : 0,
30146     dragable : false,
30147     pinching : false,
30148     mouseX : 0,
30149     mouseY : 0,
30150     cropData : false,
30151     minWidth : 300,
30152     minHeight : 300,
30153     file : false,
30154     exif : {},
30155     baseRotate : 1,
30156     cropType : 'image/jpeg',
30157     buttons : false,
30158     canvasLoaded : false,
30159     isDocument : false,
30160     method : 'POST',
30161     paramName : 'imageUpload',
30162     loadMask : true,
30163     loadingText : 'Loading...',
30164     maskEl : false,
30165     
30166     getAutoCreate : function()
30167     {
30168         var cfg = {
30169             tag : 'div',
30170             cls : 'roo-upload-cropbox',
30171             cn : [
30172                 {
30173                     tag : 'input',
30174                     cls : 'roo-upload-cropbox-selector',
30175                     type : 'file'
30176                 },
30177                 {
30178                     tag : 'div',
30179                     cls : 'roo-upload-cropbox-body',
30180                     style : 'cursor:pointer',
30181                     cn : [
30182                         {
30183                             tag : 'div',
30184                             cls : 'roo-upload-cropbox-preview'
30185                         },
30186                         {
30187                             tag : 'div',
30188                             cls : 'roo-upload-cropbox-thumb'
30189                         },
30190                         {
30191                             tag : 'div',
30192                             cls : 'roo-upload-cropbox-empty-notify',
30193                             html : this.emptyText
30194                         },
30195                         {
30196                             tag : 'div',
30197                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30198                             html : this.rotateNotify
30199                         }
30200                     ]
30201                 },
30202                 {
30203                     tag : 'div',
30204                     cls : 'roo-upload-cropbox-footer',
30205                     cn : {
30206                         tag : 'div',
30207                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30208                         cn : []
30209                     }
30210                 }
30211             ]
30212         };
30213         
30214         return cfg;
30215     },
30216     
30217     onRender : function(ct, position)
30218     {
30219         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30220         
30221         if (this.buttons.length) {
30222             
30223             Roo.each(this.buttons, function(bb) {
30224                 
30225                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30226                 
30227                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30228                 
30229             }, this);
30230         }
30231         
30232         if(this.loadMask){
30233             this.maskEl = this.el;
30234         }
30235     },
30236     
30237     initEvents : function()
30238     {
30239         this.urlAPI = (window.createObjectURL && window) || 
30240                                 (window.URL && URL.revokeObjectURL && URL) || 
30241                                 (window.webkitURL && webkitURL);
30242                         
30243         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30244         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30245         
30246         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30247         this.selectorEl.hide();
30248         
30249         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30250         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30251         
30252         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30253         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254         this.thumbEl.hide();
30255         
30256         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30257         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30258         
30259         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30260         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30261         this.errorEl.hide();
30262         
30263         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30264         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30265         this.footerEl.hide();
30266         
30267         this.setThumbBoxSize();
30268         
30269         this.bind();
30270         
30271         this.resize();
30272         
30273         this.fireEvent('initial', this);
30274     },
30275
30276     bind : function()
30277     {
30278         var _this = this;
30279         
30280         window.addEventListener("resize", function() { _this.resize(); } );
30281         
30282         this.bodyEl.on('click', this.beforeSelectFile, this);
30283         
30284         if(Roo.isTouch){
30285             this.bodyEl.on('touchstart', this.onTouchStart, this);
30286             this.bodyEl.on('touchmove', this.onTouchMove, this);
30287             this.bodyEl.on('touchend', this.onTouchEnd, this);
30288         }
30289         
30290         if(!Roo.isTouch){
30291             this.bodyEl.on('mousedown', this.onMouseDown, this);
30292             this.bodyEl.on('mousemove', this.onMouseMove, this);
30293             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30294             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30295             Roo.get(document).on('mouseup', this.onMouseUp, this);
30296         }
30297         
30298         this.selectorEl.on('change', this.onFileSelected, this);
30299     },
30300     
30301     reset : function()
30302     {    
30303         this.scale = 0;
30304         this.baseScale = 1;
30305         this.rotate = 0;
30306         this.baseRotate = 1;
30307         this.dragable = false;
30308         this.pinching = false;
30309         this.mouseX = 0;
30310         this.mouseY = 0;
30311         this.cropData = false;
30312         this.notifyEl.dom.innerHTML = this.emptyText;
30313         
30314         this.selectorEl.dom.value = '';
30315         
30316     },
30317     
30318     resize : function()
30319     {
30320         if(this.fireEvent('resize', this) != false){
30321             this.setThumbBoxPosition();
30322             this.setCanvasPosition();
30323         }
30324     },
30325     
30326     onFooterButtonClick : function(e, el, o, type)
30327     {
30328         switch (type) {
30329             case 'rotate-left' :
30330                 this.onRotateLeft(e);
30331                 break;
30332             case 'rotate-right' :
30333                 this.onRotateRight(e);
30334                 break;
30335             case 'picture' :
30336                 this.beforeSelectFile(e);
30337                 break;
30338             case 'trash' :
30339                 this.trash(e);
30340                 break;
30341             case 'crop' :
30342                 this.crop(e);
30343                 break;
30344             case 'download' :
30345                 this.download(e);
30346                 break;
30347             default :
30348                 break;
30349         }
30350         
30351         this.fireEvent('footerbuttonclick', this, type);
30352     },
30353     
30354     beforeSelectFile : function(e)
30355     {
30356         e.preventDefault();
30357         
30358         if(this.fireEvent('beforeselectfile', this) != false){
30359             this.selectorEl.dom.click();
30360         }
30361     },
30362     
30363     onFileSelected : function(e)
30364     {
30365         e.preventDefault();
30366         
30367         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30368             return;
30369         }
30370         
30371         var file = this.selectorEl.dom.files[0];
30372         
30373         if(this.fireEvent('inspect', this, file) != false){
30374             this.prepare(file);
30375         }
30376         
30377     },
30378     
30379     trash : function(e)
30380     {
30381         this.fireEvent('trash', this);
30382     },
30383     
30384     download : function(e)
30385     {
30386         this.fireEvent('download', this);
30387     },
30388     
30389     loadCanvas : function(src)
30390     {   
30391         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30392             
30393             this.reset();
30394             
30395             this.imageEl = document.createElement('img');
30396             
30397             var _this = this;
30398             
30399             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30400             
30401             this.imageEl.src = src;
30402         }
30403     },
30404     
30405     onLoadCanvas : function()
30406     {   
30407         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30408         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30409         
30410         this.bodyEl.un('click', this.beforeSelectFile, this);
30411         
30412         this.notifyEl.hide();
30413         this.thumbEl.show();
30414         this.footerEl.show();
30415         
30416         this.baseRotateLevel();
30417         
30418         if(this.isDocument){
30419             this.setThumbBoxSize();
30420         }
30421         
30422         this.setThumbBoxPosition();
30423         
30424         this.baseScaleLevel();
30425         
30426         this.draw();
30427         
30428         this.resize();
30429         
30430         this.canvasLoaded = true;
30431         
30432         if(this.loadMask){
30433             this.maskEl.unmask();
30434         }
30435         
30436     },
30437     
30438     setCanvasPosition : function()
30439     {   
30440         if(!this.canvasEl){
30441             return;
30442         }
30443         
30444         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30445         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30446         
30447         this.previewEl.setLeft(pw);
30448         this.previewEl.setTop(ph);
30449         
30450     },
30451     
30452     onMouseDown : function(e)
30453     {   
30454         e.stopEvent();
30455         
30456         this.dragable = true;
30457         this.pinching = false;
30458         
30459         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30460             this.dragable = false;
30461             return;
30462         }
30463         
30464         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30465         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30466         
30467     },
30468     
30469     onMouseMove : function(e)
30470     {   
30471         e.stopEvent();
30472         
30473         if(!this.canvasLoaded){
30474             return;
30475         }
30476         
30477         if (!this.dragable){
30478             return;
30479         }
30480         
30481         var minX = Math.ceil(this.thumbEl.getLeft(true));
30482         var minY = Math.ceil(this.thumbEl.getTop(true));
30483         
30484         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30485         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30486         
30487         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30488         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30489         
30490         x = x - this.mouseX;
30491         y = y - this.mouseY;
30492         
30493         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30494         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30495         
30496         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30497         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30498         
30499         this.previewEl.setLeft(bgX);
30500         this.previewEl.setTop(bgY);
30501         
30502         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30503         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30504     },
30505     
30506     onMouseUp : function(e)
30507     {   
30508         e.stopEvent();
30509         
30510         this.dragable = false;
30511     },
30512     
30513     onMouseWheel : function(e)
30514     {   
30515         e.stopEvent();
30516         
30517         this.startScale = this.scale;
30518         
30519         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30520         
30521         if(!this.zoomable()){
30522             this.scale = this.startScale;
30523             return;
30524         }
30525         
30526         this.draw();
30527         
30528         return;
30529     },
30530     
30531     zoomable : function()
30532     {
30533         var minScale = this.thumbEl.getWidth() / this.minWidth;
30534         
30535         if(this.minWidth < this.minHeight){
30536             minScale = this.thumbEl.getHeight() / this.minHeight;
30537         }
30538         
30539         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30540         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30541         
30542         if(
30543                 this.isDocument &&
30544                 (this.rotate == 0 || this.rotate == 180) && 
30545                 (
30546                     width > this.imageEl.OriginWidth || 
30547                     height > this.imageEl.OriginHeight ||
30548                     (width < this.minWidth && height < this.minHeight)
30549                 )
30550         ){
30551             return false;
30552         }
30553         
30554         if(
30555                 this.isDocument &&
30556                 (this.rotate == 90 || this.rotate == 270) && 
30557                 (
30558                     width > this.imageEl.OriginWidth || 
30559                     height > this.imageEl.OriginHeight ||
30560                     (width < this.minHeight && height < this.minWidth)
30561                 )
30562         ){
30563             return false;
30564         }
30565         
30566         if(
30567                 !this.isDocument &&
30568                 (this.rotate == 0 || this.rotate == 180) && 
30569                 (
30570                     width < this.minWidth || 
30571                     width > this.imageEl.OriginWidth || 
30572                     height < this.minHeight || 
30573                     height > this.imageEl.OriginHeight
30574                 )
30575         ){
30576             return false;
30577         }
30578         
30579         if(
30580                 !this.isDocument &&
30581                 (this.rotate == 90 || this.rotate == 270) && 
30582                 (
30583                     width < this.minHeight || 
30584                     width > this.imageEl.OriginWidth || 
30585                     height < this.minWidth || 
30586                     height > this.imageEl.OriginHeight
30587                 )
30588         ){
30589             return false;
30590         }
30591         
30592         return true;
30593         
30594     },
30595     
30596     onRotateLeft : function(e)
30597     {   
30598         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30599             
30600             var minScale = this.thumbEl.getWidth() / this.minWidth;
30601             
30602             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30603             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30604             
30605             this.startScale = this.scale;
30606             
30607             while (this.getScaleLevel() < minScale){
30608             
30609                 this.scale = this.scale + 1;
30610                 
30611                 if(!this.zoomable()){
30612                     break;
30613                 }
30614                 
30615                 if(
30616                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30617                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30618                 ){
30619                     continue;
30620                 }
30621                 
30622                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30623
30624                 this.draw();
30625                 
30626                 return;
30627             }
30628             
30629             this.scale = this.startScale;
30630             
30631             this.onRotateFail();
30632             
30633             return false;
30634         }
30635         
30636         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30637
30638         if(this.isDocument){
30639             this.setThumbBoxSize();
30640             this.setThumbBoxPosition();
30641             this.setCanvasPosition();
30642         }
30643         
30644         this.draw();
30645         
30646         this.fireEvent('rotate', this, 'left');
30647         
30648     },
30649     
30650     onRotateRight : function(e)
30651     {
30652         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30653             
30654             var minScale = this.thumbEl.getWidth() / this.minWidth;
30655         
30656             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30657             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30658             
30659             this.startScale = this.scale;
30660             
30661             while (this.getScaleLevel() < minScale){
30662             
30663                 this.scale = this.scale + 1;
30664                 
30665                 if(!this.zoomable()){
30666                     break;
30667                 }
30668                 
30669                 if(
30670                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30671                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30672                 ){
30673                     continue;
30674                 }
30675                 
30676                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30677
30678                 this.draw();
30679                 
30680                 return;
30681             }
30682             
30683             this.scale = this.startScale;
30684             
30685             this.onRotateFail();
30686             
30687             return false;
30688         }
30689         
30690         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30691
30692         if(this.isDocument){
30693             this.setThumbBoxSize();
30694             this.setThumbBoxPosition();
30695             this.setCanvasPosition();
30696         }
30697         
30698         this.draw();
30699         
30700         this.fireEvent('rotate', this, 'right');
30701     },
30702     
30703     onRotateFail : function()
30704     {
30705         this.errorEl.show(true);
30706         
30707         var _this = this;
30708         
30709         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30710     },
30711     
30712     draw : function()
30713     {
30714         this.previewEl.dom.innerHTML = '';
30715         
30716         var canvasEl = document.createElement("canvas");
30717         
30718         var contextEl = canvasEl.getContext("2d");
30719         
30720         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30721         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30722         var center = this.imageEl.OriginWidth / 2;
30723         
30724         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30725             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30726             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30727             center = this.imageEl.OriginHeight / 2;
30728         }
30729         
30730         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30731         
30732         contextEl.translate(center, center);
30733         contextEl.rotate(this.rotate * Math.PI / 180);
30734
30735         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30736         
30737         this.canvasEl = document.createElement("canvas");
30738         
30739         this.contextEl = this.canvasEl.getContext("2d");
30740         
30741         switch (this.rotate) {
30742             case 0 :
30743                 
30744                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30745                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30746                 
30747                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30748                 
30749                 break;
30750             case 90 : 
30751                 
30752                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30753                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30754                 
30755                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30756                     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);
30757                     break;
30758                 }
30759                 
30760                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30761                 
30762                 break;
30763             case 180 :
30764                 
30765                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30766                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30767                 
30768                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30769                     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);
30770                     break;
30771                 }
30772                 
30773                 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);
30774                 
30775                 break;
30776             case 270 :
30777                 
30778                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30779                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30780         
30781                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30782                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30783                     break;
30784                 }
30785                 
30786                 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);
30787                 
30788                 break;
30789             default : 
30790                 break;
30791         }
30792         
30793         this.previewEl.appendChild(this.canvasEl);
30794         
30795         this.setCanvasPosition();
30796     },
30797     
30798     crop : function()
30799     {
30800         if(!this.canvasLoaded){
30801             return;
30802         }
30803         
30804         var imageCanvas = document.createElement("canvas");
30805         
30806         var imageContext = imageCanvas.getContext("2d");
30807         
30808         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30809         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30810         
30811         var center = imageCanvas.width / 2;
30812         
30813         imageContext.translate(center, center);
30814         
30815         imageContext.rotate(this.rotate * Math.PI / 180);
30816         
30817         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30818         
30819         var canvas = document.createElement("canvas");
30820         
30821         var context = canvas.getContext("2d");
30822                 
30823         canvas.width = this.minWidth;
30824         canvas.height = this.minHeight;
30825
30826         switch (this.rotate) {
30827             case 0 :
30828                 
30829                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30830                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30831                 
30832                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30833                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30834                 
30835                 var targetWidth = this.minWidth - 2 * x;
30836                 var targetHeight = this.minHeight - 2 * y;
30837                 
30838                 var scale = 1;
30839                 
30840                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30841                     scale = targetWidth / width;
30842                 }
30843                 
30844                 if(x > 0 && y == 0){
30845                     scale = targetHeight / height;
30846                 }
30847                 
30848                 if(x > 0 && y > 0){
30849                     scale = targetWidth / width;
30850                     
30851                     if(width < height){
30852                         scale = targetHeight / height;
30853                     }
30854                 }
30855                 
30856                 context.scale(scale, scale);
30857                 
30858                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30859                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30860
30861                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30862                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30863
30864                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30865                 
30866                 break;
30867             case 90 : 
30868                 
30869                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30870                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30871                 
30872                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30873                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30874                 
30875                 var targetWidth = this.minWidth - 2 * x;
30876                 var targetHeight = this.minHeight - 2 * y;
30877                 
30878                 var scale = 1;
30879                 
30880                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30881                     scale = targetWidth / width;
30882                 }
30883                 
30884                 if(x > 0 && y == 0){
30885                     scale = targetHeight / height;
30886                 }
30887                 
30888                 if(x > 0 && y > 0){
30889                     scale = targetWidth / width;
30890                     
30891                     if(width < height){
30892                         scale = targetHeight / height;
30893                     }
30894                 }
30895                 
30896                 context.scale(scale, scale);
30897                 
30898                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30899                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30900
30901                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30902                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30903                 
30904                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30905                 
30906                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30907                 
30908                 break;
30909             case 180 :
30910                 
30911                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30912                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30913                 
30914                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30915                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30916                 
30917                 var targetWidth = this.minWidth - 2 * x;
30918                 var targetHeight = this.minHeight - 2 * y;
30919                 
30920                 var scale = 1;
30921                 
30922                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30923                     scale = targetWidth / width;
30924                 }
30925                 
30926                 if(x > 0 && y == 0){
30927                     scale = targetHeight / height;
30928                 }
30929                 
30930                 if(x > 0 && y > 0){
30931                     scale = targetWidth / width;
30932                     
30933                     if(width < height){
30934                         scale = targetHeight / height;
30935                     }
30936                 }
30937                 
30938                 context.scale(scale, scale);
30939                 
30940                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30941                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30942
30943                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30944                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30945
30946                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30947                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30948                 
30949                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30950                 
30951                 break;
30952             case 270 :
30953                 
30954                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30955                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30956                 
30957                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30958                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30959                 
30960                 var targetWidth = this.minWidth - 2 * x;
30961                 var targetHeight = this.minHeight - 2 * y;
30962                 
30963                 var scale = 1;
30964                 
30965                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30966                     scale = targetWidth / width;
30967                 }
30968                 
30969                 if(x > 0 && y == 0){
30970                     scale = targetHeight / height;
30971                 }
30972                 
30973                 if(x > 0 && y > 0){
30974                     scale = targetWidth / width;
30975                     
30976                     if(width < height){
30977                         scale = targetHeight / height;
30978                     }
30979                 }
30980                 
30981                 context.scale(scale, scale);
30982                 
30983                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30984                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30985
30986                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30987                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30988                 
30989                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30990                 
30991                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30992                 
30993                 break;
30994             default : 
30995                 break;
30996         }
30997         
30998         this.cropData = canvas.toDataURL(this.cropType);
30999         
31000         if(this.fireEvent('crop', this, this.cropData) !== false){
31001             this.process(this.file, this.cropData);
31002         }
31003         
31004         return;
31005         
31006     },
31007     
31008     setThumbBoxSize : function()
31009     {
31010         var width, height;
31011         
31012         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31013             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31014             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31015             
31016             this.minWidth = width;
31017             this.minHeight = height;
31018             
31019             if(this.rotate == 90 || this.rotate == 270){
31020                 this.minWidth = height;
31021                 this.minHeight = width;
31022             }
31023         }
31024         
31025         height = 300;
31026         width = Math.ceil(this.minWidth * height / this.minHeight);
31027         
31028         if(this.minWidth > this.minHeight){
31029             width = 300;
31030             height = Math.ceil(this.minHeight * width / this.minWidth);
31031         }
31032         
31033         this.thumbEl.setStyle({
31034             width : width + 'px',
31035             height : height + 'px'
31036         });
31037
31038         return;
31039             
31040     },
31041     
31042     setThumbBoxPosition : function()
31043     {
31044         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31045         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31046         
31047         this.thumbEl.setLeft(x);
31048         this.thumbEl.setTop(y);
31049         
31050     },
31051     
31052     baseRotateLevel : function()
31053     {
31054         this.baseRotate = 1;
31055         
31056         if(
31057                 typeof(this.exif) != 'undefined' &&
31058                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31059                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31060         ){
31061             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31062         }
31063         
31064         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31065         
31066     },
31067     
31068     baseScaleLevel : function()
31069     {
31070         var width, height;
31071         
31072         if(this.isDocument){
31073             
31074             if(this.baseRotate == 6 || this.baseRotate == 8){
31075             
31076                 height = this.thumbEl.getHeight();
31077                 this.baseScale = height / this.imageEl.OriginWidth;
31078
31079                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31080                     width = this.thumbEl.getWidth();
31081                     this.baseScale = width / this.imageEl.OriginHeight;
31082                 }
31083
31084                 return;
31085             }
31086
31087             height = this.thumbEl.getHeight();
31088             this.baseScale = height / this.imageEl.OriginHeight;
31089
31090             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31091                 width = this.thumbEl.getWidth();
31092                 this.baseScale = width / this.imageEl.OriginWidth;
31093             }
31094
31095             return;
31096         }
31097         
31098         if(this.baseRotate == 6 || this.baseRotate == 8){
31099             
31100             width = this.thumbEl.getHeight();
31101             this.baseScale = width / this.imageEl.OriginHeight;
31102             
31103             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31104                 height = this.thumbEl.getWidth();
31105                 this.baseScale = height / this.imageEl.OriginHeight;
31106             }
31107             
31108             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31109                 height = this.thumbEl.getWidth();
31110                 this.baseScale = height / this.imageEl.OriginHeight;
31111                 
31112                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31113                     width = this.thumbEl.getHeight();
31114                     this.baseScale = width / this.imageEl.OriginWidth;
31115                 }
31116             }
31117             
31118             return;
31119         }
31120         
31121         width = this.thumbEl.getWidth();
31122         this.baseScale = width / this.imageEl.OriginWidth;
31123         
31124         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31125             height = this.thumbEl.getHeight();
31126             this.baseScale = height / this.imageEl.OriginHeight;
31127         }
31128         
31129         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31130             
31131             height = this.thumbEl.getHeight();
31132             this.baseScale = height / this.imageEl.OriginHeight;
31133             
31134             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31135                 width = this.thumbEl.getWidth();
31136                 this.baseScale = width / this.imageEl.OriginWidth;
31137             }
31138             
31139         }
31140         
31141         return;
31142     },
31143     
31144     getScaleLevel : function()
31145     {
31146         return this.baseScale * Math.pow(1.1, this.scale);
31147     },
31148     
31149     onTouchStart : function(e)
31150     {
31151         if(!this.canvasLoaded){
31152             this.beforeSelectFile(e);
31153             return;
31154         }
31155         
31156         var touches = e.browserEvent.touches;
31157         
31158         if(!touches){
31159             return;
31160         }
31161         
31162         if(touches.length == 1){
31163             this.onMouseDown(e);
31164             return;
31165         }
31166         
31167         if(touches.length != 2){
31168             return;
31169         }
31170         
31171         var coords = [];
31172         
31173         for(var i = 0, finger; finger = touches[i]; i++){
31174             coords.push(finger.pageX, finger.pageY);
31175         }
31176         
31177         var x = Math.pow(coords[0] - coords[2], 2);
31178         var y = Math.pow(coords[1] - coords[3], 2);
31179         
31180         this.startDistance = Math.sqrt(x + y);
31181         
31182         this.startScale = this.scale;
31183         
31184         this.pinching = true;
31185         this.dragable = false;
31186         
31187     },
31188     
31189     onTouchMove : function(e)
31190     {
31191         if(!this.pinching && !this.dragable){
31192             return;
31193         }
31194         
31195         var touches = e.browserEvent.touches;
31196         
31197         if(!touches){
31198             return;
31199         }
31200         
31201         if(this.dragable){
31202             this.onMouseMove(e);
31203             return;
31204         }
31205         
31206         var coords = [];
31207         
31208         for(var i = 0, finger; finger = touches[i]; i++){
31209             coords.push(finger.pageX, finger.pageY);
31210         }
31211         
31212         var x = Math.pow(coords[0] - coords[2], 2);
31213         var y = Math.pow(coords[1] - coords[3], 2);
31214         
31215         this.endDistance = Math.sqrt(x + y);
31216         
31217         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31218         
31219         if(!this.zoomable()){
31220             this.scale = this.startScale;
31221             return;
31222         }
31223         
31224         this.draw();
31225         
31226     },
31227     
31228     onTouchEnd : function(e)
31229     {
31230         this.pinching = false;
31231         this.dragable = false;
31232         
31233     },
31234     
31235     process : function(file, crop)
31236     {
31237         if(this.loadMask){
31238             this.maskEl.mask(this.loadingText);
31239         }
31240         
31241         this.xhr = new XMLHttpRequest();
31242         
31243         file.xhr = this.xhr;
31244
31245         this.xhr.open(this.method, this.url, true);
31246         
31247         var headers = {
31248             "Accept": "application/json",
31249             "Cache-Control": "no-cache",
31250             "X-Requested-With": "XMLHttpRequest"
31251         };
31252         
31253         for (var headerName in headers) {
31254             var headerValue = headers[headerName];
31255             if (headerValue) {
31256                 this.xhr.setRequestHeader(headerName, headerValue);
31257             }
31258         }
31259         
31260         var _this = this;
31261         
31262         this.xhr.onload = function()
31263         {
31264             _this.xhrOnLoad(_this.xhr);
31265         }
31266         
31267         this.xhr.onerror = function()
31268         {
31269             _this.xhrOnError(_this.xhr);
31270         }
31271         
31272         var formData = new FormData();
31273
31274         formData.append('returnHTML', 'NO');
31275         
31276         if(crop){
31277             formData.append('crop', crop);
31278         }
31279         
31280         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31281             formData.append(this.paramName, file, file.name);
31282         }
31283         
31284         if(typeof(file.filename) != 'undefined'){
31285             formData.append('filename', file.filename);
31286         }
31287         
31288         if(typeof(file.mimetype) != 'undefined'){
31289             formData.append('mimetype', file.mimetype);
31290         }
31291         
31292         if(this.fireEvent('arrange', this, formData) != false){
31293             this.xhr.send(formData);
31294         };
31295     },
31296     
31297     xhrOnLoad : function(xhr)
31298     {
31299         if(this.loadMask){
31300             this.maskEl.unmask();
31301         }
31302         
31303         if (xhr.readyState !== 4) {
31304             this.fireEvent('exception', this, xhr);
31305             return;
31306         }
31307
31308         var response = Roo.decode(xhr.responseText);
31309         
31310         if(!response.success){
31311             this.fireEvent('exception', this, xhr);
31312             return;
31313         }
31314         
31315         var response = Roo.decode(xhr.responseText);
31316         
31317         this.fireEvent('upload', this, response);
31318         
31319     },
31320     
31321     xhrOnError : function()
31322     {
31323         if(this.loadMask){
31324             this.maskEl.unmask();
31325         }
31326         
31327         Roo.log('xhr on error');
31328         
31329         var response = Roo.decode(xhr.responseText);
31330           
31331         Roo.log(response);
31332         
31333     },
31334     
31335     prepare : function(file)
31336     {   
31337         if(this.loadMask){
31338             this.maskEl.mask(this.loadingText);
31339         }
31340         
31341         this.file = false;
31342         this.exif = {};
31343         
31344         if(typeof(file) === 'string'){
31345             this.loadCanvas(file);
31346             return;
31347         }
31348         
31349         if(!file || !this.urlAPI){
31350             return;
31351         }
31352         
31353         this.file = file;
31354         this.cropType = file.type;
31355         
31356         var _this = this;
31357         
31358         if(this.fireEvent('prepare', this, this.file) != false){
31359             
31360             var reader = new FileReader();
31361             
31362             reader.onload = function (e) {
31363                 if (e.target.error) {
31364                     Roo.log(e.target.error);
31365                     return;
31366                 }
31367                 
31368                 var buffer = e.target.result,
31369                     dataView = new DataView(buffer),
31370                     offset = 2,
31371                     maxOffset = dataView.byteLength - 4,
31372                     markerBytes,
31373                     markerLength;
31374                 
31375                 if (dataView.getUint16(0) === 0xffd8) {
31376                     while (offset < maxOffset) {
31377                         markerBytes = dataView.getUint16(offset);
31378                         
31379                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31380                             markerLength = dataView.getUint16(offset + 2) + 2;
31381                             if (offset + markerLength > dataView.byteLength) {
31382                                 Roo.log('Invalid meta data: Invalid segment size.');
31383                                 break;
31384                             }
31385                             
31386                             if(markerBytes == 0xffe1){
31387                                 _this.parseExifData(
31388                                     dataView,
31389                                     offset,
31390                                     markerLength
31391                                 );
31392                             }
31393                             
31394                             offset += markerLength;
31395                             
31396                             continue;
31397                         }
31398                         
31399                         break;
31400                     }
31401                     
31402                 }
31403                 
31404                 var url = _this.urlAPI.createObjectURL(_this.file);
31405                 
31406                 _this.loadCanvas(url);
31407                 
31408                 return;
31409             }
31410             
31411             reader.readAsArrayBuffer(this.file);
31412             
31413         }
31414         
31415     },
31416     
31417     parseExifData : function(dataView, offset, length)
31418     {
31419         var tiffOffset = offset + 10,
31420             littleEndian,
31421             dirOffset;
31422     
31423         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31424             // No Exif data, might be XMP data instead
31425             return;
31426         }
31427         
31428         // Check for the ASCII code for "Exif" (0x45786966):
31429         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31430             // No Exif data, might be XMP data instead
31431             return;
31432         }
31433         if (tiffOffset + 8 > dataView.byteLength) {
31434             Roo.log('Invalid Exif data: Invalid segment size.');
31435             return;
31436         }
31437         // Check for the two null bytes:
31438         if (dataView.getUint16(offset + 8) !== 0x0000) {
31439             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31440             return;
31441         }
31442         // Check the byte alignment:
31443         switch (dataView.getUint16(tiffOffset)) {
31444         case 0x4949:
31445             littleEndian = true;
31446             break;
31447         case 0x4D4D:
31448             littleEndian = false;
31449             break;
31450         default:
31451             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31452             return;
31453         }
31454         // Check for the TIFF tag marker (0x002A):
31455         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31456             Roo.log('Invalid Exif data: Missing TIFF marker.');
31457             return;
31458         }
31459         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31460         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31461         
31462         this.parseExifTags(
31463             dataView,
31464             tiffOffset,
31465             tiffOffset + dirOffset,
31466             littleEndian
31467         );
31468     },
31469     
31470     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31471     {
31472         var tagsNumber,
31473             dirEndOffset,
31474             i;
31475         if (dirOffset + 6 > dataView.byteLength) {
31476             Roo.log('Invalid Exif data: Invalid directory offset.');
31477             return;
31478         }
31479         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31480         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31481         if (dirEndOffset + 4 > dataView.byteLength) {
31482             Roo.log('Invalid Exif data: Invalid directory size.');
31483             return;
31484         }
31485         for (i = 0; i < tagsNumber; i += 1) {
31486             this.parseExifTag(
31487                 dataView,
31488                 tiffOffset,
31489                 dirOffset + 2 + 12 * i, // tag offset
31490                 littleEndian
31491             );
31492         }
31493         // Return the offset to the next directory:
31494         return dataView.getUint32(dirEndOffset, littleEndian);
31495     },
31496     
31497     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31498     {
31499         var tag = dataView.getUint16(offset, littleEndian);
31500         
31501         this.exif[tag] = this.getExifValue(
31502             dataView,
31503             tiffOffset,
31504             offset,
31505             dataView.getUint16(offset + 2, littleEndian), // tag type
31506             dataView.getUint32(offset + 4, littleEndian), // tag length
31507             littleEndian
31508         );
31509     },
31510     
31511     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31512     {
31513         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31514             tagSize,
31515             dataOffset,
31516             values,
31517             i,
31518             str,
31519             c;
31520     
31521         if (!tagType) {
31522             Roo.log('Invalid Exif data: Invalid tag type.');
31523             return;
31524         }
31525         
31526         tagSize = tagType.size * length;
31527         // Determine if the value is contained in the dataOffset bytes,
31528         // or if the value at the dataOffset is a pointer to the actual data:
31529         dataOffset = tagSize > 4 ?
31530                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31531         if (dataOffset + tagSize > dataView.byteLength) {
31532             Roo.log('Invalid Exif data: Invalid data offset.');
31533             return;
31534         }
31535         if (length === 1) {
31536             return tagType.getValue(dataView, dataOffset, littleEndian);
31537         }
31538         values = [];
31539         for (i = 0; i < length; i += 1) {
31540             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31541         }
31542         
31543         if (tagType.ascii) {
31544             str = '';
31545             // Concatenate the chars:
31546             for (i = 0; i < values.length; i += 1) {
31547                 c = values[i];
31548                 // Ignore the terminating NULL byte(s):
31549                 if (c === '\u0000') {
31550                     break;
31551                 }
31552                 str += c;
31553             }
31554             return str;
31555         }
31556         return values;
31557     }
31558     
31559 });
31560
31561 Roo.apply(Roo.bootstrap.UploadCropbox, {
31562     tags : {
31563         'Orientation': 0x0112
31564     },
31565     
31566     Orientation: {
31567             1: 0, //'top-left',
31568 //            2: 'top-right',
31569             3: 180, //'bottom-right',
31570 //            4: 'bottom-left',
31571 //            5: 'left-top',
31572             6: 90, //'right-top',
31573 //            7: 'right-bottom',
31574             8: 270 //'left-bottom'
31575     },
31576     
31577     exifTagTypes : {
31578         // byte, 8-bit unsigned int:
31579         1: {
31580             getValue: function (dataView, dataOffset) {
31581                 return dataView.getUint8(dataOffset);
31582             },
31583             size: 1
31584         },
31585         // ascii, 8-bit byte:
31586         2: {
31587             getValue: function (dataView, dataOffset) {
31588                 return String.fromCharCode(dataView.getUint8(dataOffset));
31589             },
31590             size: 1,
31591             ascii: true
31592         },
31593         // short, 16 bit int:
31594         3: {
31595             getValue: function (dataView, dataOffset, littleEndian) {
31596                 return dataView.getUint16(dataOffset, littleEndian);
31597             },
31598             size: 2
31599         },
31600         // long, 32 bit int:
31601         4: {
31602             getValue: function (dataView, dataOffset, littleEndian) {
31603                 return dataView.getUint32(dataOffset, littleEndian);
31604             },
31605             size: 4
31606         },
31607         // rational = two long values, first is numerator, second is denominator:
31608         5: {
31609             getValue: function (dataView, dataOffset, littleEndian) {
31610                 return dataView.getUint32(dataOffset, littleEndian) /
31611                     dataView.getUint32(dataOffset + 4, littleEndian);
31612             },
31613             size: 8
31614         },
31615         // slong, 32 bit signed int:
31616         9: {
31617             getValue: function (dataView, dataOffset, littleEndian) {
31618                 return dataView.getInt32(dataOffset, littleEndian);
31619             },
31620             size: 4
31621         },
31622         // srational, two slongs, first is numerator, second is denominator:
31623         10: {
31624             getValue: function (dataView, dataOffset, littleEndian) {
31625                 return dataView.getInt32(dataOffset, littleEndian) /
31626                     dataView.getInt32(dataOffset + 4, littleEndian);
31627             },
31628             size: 8
31629         }
31630     },
31631     
31632     footer : {
31633         STANDARD : [
31634             {
31635                 tag : 'div',
31636                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31637                 action : 'rotate-left',
31638                 cn : [
31639                     {
31640                         tag : 'button',
31641                         cls : 'btn btn-default',
31642                         html : '<i class="fa fa-undo"></i>'
31643                     }
31644                 ]
31645             },
31646             {
31647                 tag : 'div',
31648                 cls : 'btn-group roo-upload-cropbox-picture',
31649                 action : 'picture',
31650                 cn : [
31651                     {
31652                         tag : 'button',
31653                         cls : 'btn btn-default',
31654                         html : '<i class="fa fa-picture-o"></i>'
31655                     }
31656                 ]
31657             },
31658             {
31659                 tag : 'div',
31660                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31661                 action : 'rotate-right',
31662                 cn : [
31663                     {
31664                         tag : 'button',
31665                         cls : 'btn btn-default',
31666                         html : '<i class="fa fa-repeat"></i>'
31667                     }
31668                 ]
31669             }
31670         ],
31671         DOCUMENT : [
31672             {
31673                 tag : 'div',
31674                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31675                 action : 'rotate-left',
31676                 cn : [
31677                     {
31678                         tag : 'button',
31679                         cls : 'btn btn-default',
31680                         html : '<i class="fa fa-undo"></i>'
31681                     }
31682                 ]
31683             },
31684             {
31685                 tag : 'div',
31686                 cls : 'btn-group roo-upload-cropbox-download',
31687                 action : 'download',
31688                 cn : [
31689                     {
31690                         tag : 'button',
31691                         cls : 'btn btn-default',
31692                         html : '<i class="fa fa-download"></i>'
31693                     }
31694                 ]
31695             },
31696             {
31697                 tag : 'div',
31698                 cls : 'btn-group roo-upload-cropbox-crop',
31699                 action : 'crop',
31700                 cn : [
31701                     {
31702                         tag : 'button',
31703                         cls : 'btn btn-default',
31704                         html : '<i class="fa fa-crop"></i>'
31705                     }
31706                 ]
31707             },
31708             {
31709                 tag : 'div',
31710                 cls : 'btn-group roo-upload-cropbox-trash',
31711                 action : 'trash',
31712                 cn : [
31713                     {
31714                         tag : 'button',
31715                         cls : 'btn btn-default',
31716                         html : '<i class="fa fa-trash"></i>'
31717                     }
31718                 ]
31719             },
31720             {
31721                 tag : 'div',
31722                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31723                 action : 'rotate-right',
31724                 cn : [
31725                     {
31726                         tag : 'button',
31727                         cls : 'btn btn-default',
31728                         html : '<i class="fa fa-repeat"></i>'
31729                     }
31730                 ]
31731             }
31732         ],
31733         ROTATOR : [
31734             {
31735                 tag : 'div',
31736                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31737                 action : 'rotate-left',
31738                 cn : [
31739                     {
31740                         tag : 'button',
31741                         cls : 'btn btn-default',
31742                         html : '<i class="fa fa-undo"></i>'
31743                     }
31744                 ]
31745             },
31746             {
31747                 tag : 'div',
31748                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31749                 action : 'rotate-right',
31750                 cn : [
31751                     {
31752                         tag : 'button',
31753                         cls : 'btn btn-default',
31754                         html : '<i class="fa fa-repeat"></i>'
31755                     }
31756                 ]
31757             }
31758         ]
31759     }
31760 });
31761
31762 /*
31763 * Licence: LGPL
31764 */
31765
31766 /**
31767  * @class Roo.bootstrap.DocumentManager
31768  * @extends Roo.bootstrap.Component
31769  * Bootstrap DocumentManager class
31770  * @cfg {String} paramName default 'imageUpload'
31771  * @cfg {String} toolTipName default 'filename'
31772  * @cfg {String} method default POST
31773  * @cfg {String} url action url
31774  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31775  * @cfg {Boolean} multiple multiple upload default true
31776  * @cfg {Number} thumbSize default 300
31777  * @cfg {String} fieldLabel
31778  * @cfg {Number} labelWidth default 4
31779  * @cfg {String} labelAlign (left|top) default left
31780  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31781 * @cfg {Number} labellg set the width of label (1-12)
31782  * @cfg {Number} labelmd set the width of label (1-12)
31783  * @cfg {Number} labelsm set the width of label (1-12)
31784  * @cfg {Number} labelxs set the width of label (1-12)
31785  * 
31786  * @constructor
31787  * Create a new DocumentManager
31788  * @param {Object} config The config object
31789  */
31790
31791 Roo.bootstrap.DocumentManager = function(config){
31792     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31793     
31794     this.files = [];
31795     this.delegates = [];
31796     
31797     this.addEvents({
31798         /**
31799          * @event initial
31800          * Fire when initial the DocumentManager
31801          * @param {Roo.bootstrap.DocumentManager} this
31802          */
31803         "initial" : true,
31804         /**
31805          * @event inspect
31806          * inspect selected file
31807          * @param {Roo.bootstrap.DocumentManager} this
31808          * @param {File} file
31809          */
31810         "inspect" : true,
31811         /**
31812          * @event exception
31813          * Fire when xhr load exception
31814          * @param {Roo.bootstrap.DocumentManager} this
31815          * @param {XMLHttpRequest} xhr
31816          */
31817         "exception" : true,
31818         /**
31819          * @event afterupload
31820          * Fire when xhr load exception
31821          * @param {Roo.bootstrap.DocumentManager} this
31822          * @param {XMLHttpRequest} xhr
31823          */
31824         "afterupload" : true,
31825         /**
31826          * @event prepare
31827          * prepare the form data
31828          * @param {Roo.bootstrap.DocumentManager} this
31829          * @param {Object} formData
31830          */
31831         "prepare" : true,
31832         /**
31833          * @event remove
31834          * Fire when remove the file
31835          * @param {Roo.bootstrap.DocumentManager} this
31836          * @param {Object} file
31837          */
31838         "remove" : true,
31839         /**
31840          * @event refresh
31841          * Fire after refresh the file
31842          * @param {Roo.bootstrap.DocumentManager} this
31843          */
31844         "refresh" : true,
31845         /**
31846          * @event click
31847          * Fire after click the image
31848          * @param {Roo.bootstrap.DocumentManager} this
31849          * @param {Object} file
31850          */
31851         "click" : true,
31852         /**
31853          * @event edit
31854          * Fire when upload a image and editable set to true
31855          * @param {Roo.bootstrap.DocumentManager} this
31856          * @param {Object} file
31857          */
31858         "edit" : true,
31859         /**
31860          * @event beforeselectfile
31861          * Fire before select file
31862          * @param {Roo.bootstrap.DocumentManager} this
31863          */
31864         "beforeselectfile" : true,
31865         /**
31866          * @event process
31867          * Fire before process file
31868          * @param {Roo.bootstrap.DocumentManager} this
31869          * @param {Object} file
31870          */
31871         "process" : true,
31872         /**
31873          * @event previewrendered
31874          * Fire when preview rendered
31875          * @param {Roo.bootstrap.DocumentManager} this
31876          * @param {Object} file
31877          */
31878         "previewrendered" : true,
31879         /**
31880          */
31881         "previewResize" : true
31882         
31883     });
31884 };
31885
31886 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31887     
31888     boxes : 0,
31889     inputName : '',
31890     thumbSize : 300,
31891     multiple : true,
31892     files : false,
31893     method : 'POST',
31894     url : '',
31895     paramName : 'imageUpload',
31896     toolTipName : 'filename',
31897     fieldLabel : '',
31898     labelWidth : 4,
31899     labelAlign : 'left',
31900     editable : true,
31901     delegates : false,
31902     xhr : false, 
31903     
31904     labellg : 0,
31905     labelmd : 0,
31906     labelsm : 0,
31907     labelxs : 0,
31908     
31909     getAutoCreate : function()
31910     {   
31911         var managerWidget = {
31912             tag : 'div',
31913             cls : 'roo-document-manager',
31914             cn : [
31915                 {
31916                     tag : 'input',
31917                     cls : 'roo-document-manager-selector',
31918                     type : 'file'
31919                 },
31920                 {
31921                     tag : 'div',
31922                     cls : 'roo-document-manager-uploader',
31923                     cn : [
31924                         {
31925                             tag : 'div',
31926                             cls : 'roo-document-manager-upload-btn',
31927                             html : '<i class="fa fa-plus"></i>'
31928                         }
31929                     ]
31930                     
31931                 }
31932             ]
31933         };
31934         
31935         var content = [
31936             {
31937                 tag : 'div',
31938                 cls : 'column col-md-12',
31939                 cn : managerWidget
31940             }
31941         ];
31942         
31943         if(this.fieldLabel.length){
31944             
31945             content = [
31946                 {
31947                     tag : 'div',
31948                     cls : 'column col-md-12',
31949                     html : this.fieldLabel
31950                 },
31951                 {
31952                     tag : 'div',
31953                     cls : 'column col-md-12',
31954                     cn : managerWidget
31955                 }
31956             ];
31957
31958             if(this.labelAlign == 'left'){
31959                 content = [
31960                     {
31961                         tag : 'div',
31962                         cls : 'column',
31963                         html : this.fieldLabel
31964                     },
31965                     {
31966                         tag : 'div',
31967                         cls : 'column',
31968                         cn : managerWidget
31969                     }
31970                 ];
31971                 
31972                 if(this.labelWidth > 12){
31973                     content[0].style = "width: " + this.labelWidth + 'px';
31974                 }
31975
31976                 if(this.labelWidth < 13 && this.labelmd == 0){
31977                     this.labelmd = this.labelWidth;
31978                 }
31979
31980                 if(this.labellg > 0){
31981                     content[0].cls += ' col-lg-' + this.labellg;
31982                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31983                 }
31984
31985                 if(this.labelmd > 0){
31986                     content[0].cls += ' col-md-' + this.labelmd;
31987                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31988                 }
31989
31990                 if(this.labelsm > 0){
31991                     content[0].cls += ' col-sm-' + this.labelsm;
31992                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31993                 }
31994
31995                 if(this.labelxs > 0){
31996                     content[0].cls += ' col-xs-' + this.labelxs;
31997                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31998                 }
31999                 
32000             }
32001         }
32002         
32003         var cfg = {
32004             tag : 'div',
32005             cls : 'row clearfix',
32006             cn : content
32007         };
32008         
32009         return cfg;
32010         
32011     },
32012     
32013     initEvents : function()
32014     {
32015         this.managerEl = this.el.select('.roo-document-manager', true).first();
32016         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32017         
32018         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32019         this.selectorEl.hide();
32020         
32021         if(this.multiple){
32022             this.selectorEl.attr('multiple', 'multiple');
32023         }
32024         
32025         this.selectorEl.on('change', this.onFileSelected, this);
32026         
32027         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32028         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32029         
32030         this.uploader.on('click', this.onUploaderClick, this);
32031         
32032         this.renderProgressDialog();
32033         
32034         var _this = this;
32035         
32036         window.addEventListener("resize", function() { _this.refresh(); } );
32037         
32038         this.fireEvent('initial', this);
32039     },
32040     
32041     renderProgressDialog : function()
32042     {
32043         var _this = this;
32044         
32045         this.progressDialog = new Roo.bootstrap.Modal({
32046             cls : 'roo-document-manager-progress-dialog',
32047             allow_close : false,
32048             animate : false,
32049             title : '',
32050             buttons : [
32051                 {
32052                     name  :'cancel',
32053                     weight : 'danger',
32054                     html : 'Cancel'
32055                 }
32056             ], 
32057             listeners : { 
32058                 btnclick : function() {
32059                     _this.uploadCancel();
32060                     this.hide();
32061                 }
32062             }
32063         });
32064          
32065         this.progressDialog.render(Roo.get(document.body));
32066          
32067         this.progress = new Roo.bootstrap.Progress({
32068             cls : 'roo-document-manager-progress',
32069             active : true,
32070             striped : true
32071         });
32072         
32073         this.progress.render(this.progressDialog.getChildContainer());
32074         
32075         this.progressBar = new Roo.bootstrap.ProgressBar({
32076             cls : 'roo-document-manager-progress-bar',
32077             aria_valuenow : 0,
32078             aria_valuemin : 0,
32079             aria_valuemax : 12,
32080             panel : 'success'
32081         });
32082         
32083         this.progressBar.render(this.progress.getChildContainer());
32084     },
32085     
32086     onUploaderClick : function(e)
32087     {
32088         e.preventDefault();
32089      
32090         if(this.fireEvent('beforeselectfile', this) != false){
32091             this.selectorEl.dom.click();
32092         }
32093         
32094     },
32095     
32096     onFileSelected : function(e)
32097     {
32098         e.preventDefault();
32099         
32100         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32101             return;
32102         }
32103         
32104         Roo.each(this.selectorEl.dom.files, function(file){
32105             if(this.fireEvent('inspect', this, file) != false){
32106                 this.files.push(file);
32107             }
32108         }, this);
32109         
32110         this.queue();
32111         
32112     },
32113     
32114     queue : function()
32115     {
32116         this.selectorEl.dom.value = '';
32117         
32118         if(!this.files || !this.files.length){
32119             return;
32120         }
32121         
32122         if(this.boxes > 0 && this.files.length > this.boxes){
32123             this.files = this.files.slice(0, this.boxes);
32124         }
32125         
32126         this.uploader.show();
32127         
32128         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32129             this.uploader.hide();
32130         }
32131         
32132         var _this = this;
32133         
32134         var files = [];
32135         
32136         var docs = [];
32137         
32138         Roo.each(this.files, function(file){
32139             
32140             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32141                 var f = this.renderPreview(file);
32142                 files.push(f);
32143                 return;
32144             }
32145             
32146             if(file.type.indexOf('image') != -1){
32147                 this.delegates.push(
32148                     (function(){
32149                         _this.process(file);
32150                     }).createDelegate(this)
32151                 );
32152         
32153                 return;
32154             }
32155             
32156             docs.push(
32157                 (function(){
32158                     _this.process(file);
32159                 }).createDelegate(this)
32160             );
32161             
32162         }, this);
32163         
32164         this.files = files;
32165         
32166         this.delegates = this.delegates.concat(docs);
32167         
32168         if(!this.delegates.length){
32169             this.refresh();
32170             return;
32171         }
32172         
32173         this.progressBar.aria_valuemax = this.delegates.length;
32174         
32175         this.arrange();
32176         
32177         return;
32178     },
32179     
32180     arrange : function()
32181     {
32182         if(!this.delegates.length){
32183             this.progressDialog.hide();
32184             this.refresh();
32185             return;
32186         }
32187         
32188         var delegate = this.delegates.shift();
32189         
32190         this.progressDialog.show();
32191         
32192         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32193         
32194         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32195         
32196         delegate();
32197     },
32198     
32199     refresh : function()
32200     {
32201         this.uploader.show();
32202         
32203         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32204             this.uploader.hide();
32205         }
32206         
32207         Roo.isTouch ? this.closable(false) : this.closable(true);
32208         
32209         this.fireEvent('refresh', this);
32210     },
32211     
32212     onRemove : function(e, el, o)
32213     {
32214         e.preventDefault();
32215         
32216         this.fireEvent('remove', this, o);
32217         
32218     },
32219     
32220     remove : function(o)
32221     {
32222         var files = [];
32223         
32224         Roo.each(this.files, function(file){
32225             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32226                 files.push(file);
32227                 return;
32228             }
32229
32230             o.target.remove();
32231
32232         }, this);
32233         
32234         this.files = files;
32235         
32236         this.refresh();
32237     },
32238     
32239     clear : function()
32240     {
32241         Roo.each(this.files, function(file){
32242             if(!file.target){
32243                 return;
32244             }
32245             
32246             file.target.remove();
32247
32248         }, this);
32249         
32250         this.files = [];
32251         
32252         this.refresh();
32253     },
32254     
32255     onClick : function(e, el, o)
32256     {
32257         e.preventDefault();
32258         
32259         this.fireEvent('click', this, o);
32260         
32261     },
32262     
32263     closable : function(closable)
32264     {
32265         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32266             
32267             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32268             
32269             if(closable){
32270                 el.show();
32271                 return;
32272             }
32273             
32274             el.hide();
32275             
32276         }, this);
32277     },
32278     
32279     xhrOnLoad : function(xhr)
32280     {
32281         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32282             el.remove();
32283         }, this);
32284         
32285         if (xhr.readyState !== 4) {
32286             this.arrange();
32287             this.fireEvent('exception', this, xhr);
32288             return;
32289         }
32290
32291         var response = Roo.decode(xhr.responseText);
32292         
32293         if(!response.success){
32294             this.arrange();
32295             this.fireEvent('exception', this, xhr);
32296             return;
32297         }
32298         
32299         var file = this.renderPreview(response.data);
32300         
32301         this.files.push(file);
32302         
32303         this.arrange();
32304         
32305         this.fireEvent('afterupload', this, xhr);
32306         
32307     },
32308     
32309     xhrOnError : function(xhr)
32310     {
32311         Roo.log('xhr on error');
32312         
32313         var response = Roo.decode(xhr.responseText);
32314           
32315         Roo.log(response);
32316         
32317         this.arrange();
32318     },
32319     
32320     process : function(file)
32321     {
32322         if(this.fireEvent('process', this, file) !== false){
32323             if(this.editable && file.type.indexOf('image') != -1){
32324                 this.fireEvent('edit', this, file);
32325                 return;
32326             }
32327
32328             this.uploadStart(file, false);
32329
32330             return;
32331         }
32332         
32333     },
32334     
32335     uploadStart : function(file, crop)
32336     {
32337         this.xhr = new XMLHttpRequest();
32338         
32339         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32340             this.arrange();
32341             return;
32342         }
32343         
32344         file.xhr = this.xhr;
32345             
32346         this.managerEl.createChild({
32347             tag : 'div',
32348             cls : 'roo-document-manager-loading',
32349             cn : [
32350                 {
32351                     tag : 'div',
32352                     tooltip : file.name,
32353                     cls : 'roo-document-manager-thumb',
32354                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32355                 }
32356             ]
32357
32358         });
32359
32360         this.xhr.open(this.method, this.url, true);
32361         
32362         var headers = {
32363             "Accept": "application/json",
32364             "Cache-Control": "no-cache",
32365             "X-Requested-With": "XMLHttpRequest"
32366         };
32367         
32368         for (var headerName in headers) {
32369             var headerValue = headers[headerName];
32370             if (headerValue) {
32371                 this.xhr.setRequestHeader(headerName, headerValue);
32372             }
32373         }
32374         
32375         var _this = this;
32376         
32377         this.xhr.onload = function()
32378         {
32379             _this.xhrOnLoad(_this.xhr);
32380         }
32381         
32382         this.xhr.onerror = function()
32383         {
32384             _this.xhrOnError(_this.xhr);
32385         }
32386         
32387         var formData = new FormData();
32388
32389         formData.append('returnHTML', 'NO');
32390         
32391         if(crop){
32392             formData.append('crop', crop);
32393         }
32394         
32395         formData.append(this.paramName, file, file.name);
32396         
32397         var options = {
32398             file : file, 
32399             manually : false
32400         };
32401         
32402         if(this.fireEvent('prepare', this, formData, options) != false){
32403             
32404             if(options.manually){
32405                 return;
32406             }
32407             
32408             this.xhr.send(formData);
32409             return;
32410         };
32411         
32412         this.uploadCancel();
32413     },
32414     
32415     uploadCancel : function()
32416     {
32417         if (this.xhr) {
32418             this.xhr.abort();
32419         }
32420         
32421         this.delegates = [];
32422         
32423         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32424             el.remove();
32425         }, this);
32426         
32427         this.arrange();
32428     },
32429     
32430     renderPreview : function(file)
32431     {
32432         if(typeof(file.target) != 'undefined' && file.target){
32433             return file;
32434         }
32435         
32436         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32437         
32438         var previewEl = this.managerEl.createChild({
32439             tag : 'div',
32440             cls : 'roo-document-manager-preview',
32441             cn : [
32442                 {
32443                     tag : 'div',
32444                     tooltip : file[this.toolTipName],
32445                     cls : 'roo-document-manager-thumb',
32446                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32447                 },
32448                 {
32449                     tag : 'button',
32450                     cls : 'close',
32451                     html : '<i class="fa fa-times-circle"></i>'
32452                 }
32453             ]
32454         });
32455
32456         var close = previewEl.select('button.close', true).first();
32457
32458         close.on('click', this.onRemove, this, file);
32459
32460         file.target = previewEl;
32461
32462         var image = previewEl.select('img', true).first();
32463         
32464         var _this = this;
32465         
32466         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32467         
32468         image.on('click', this.onClick, this, file);
32469         
32470         this.fireEvent('previewrendered', this, file);
32471         
32472         return file;
32473         
32474     },
32475     
32476     onPreviewLoad : function(file, image)
32477     {
32478         if(typeof(file.target) == 'undefined' || !file.target){
32479             return;
32480         }
32481         
32482         var width = image.dom.naturalWidth || image.dom.width;
32483         var height = image.dom.naturalHeight || image.dom.height;
32484         
32485         if(!this.previewResize) {
32486             return;
32487         }
32488         
32489         if(width > height){
32490             file.target.addClass('wide');
32491             return;
32492         }
32493         
32494         file.target.addClass('tall');
32495         return;
32496         
32497     },
32498     
32499     uploadFromSource : function(file, crop)
32500     {
32501         this.xhr = new XMLHttpRequest();
32502         
32503         this.managerEl.createChild({
32504             tag : 'div',
32505             cls : 'roo-document-manager-loading',
32506             cn : [
32507                 {
32508                     tag : 'div',
32509                     tooltip : file.name,
32510                     cls : 'roo-document-manager-thumb',
32511                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32512                 }
32513             ]
32514
32515         });
32516
32517         this.xhr.open(this.method, this.url, true);
32518         
32519         var headers = {
32520             "Accept": "application/json",
32521             "Cache-Control": "no-cache",
32522             "X-Requested-With": "XMLHttpRequest"
32523         };
32524         
32525         for (var headerName in headers) {
32526             var headerValue = headers[headerName];
32527             if (headerValue) {
32528                 this.xhr.setRequestHeader(headerName, headerValue);
32529             }
32530         }
32531         
32532         var _this = this;
32533         
32534         this.xhr.onload = function()
32535         {
32536             _this.xhrOnLoad(_this.xhr);
32537         }
32538         
32539         this.xhr.onerror = function()
32540         {
32541             _this.xhrOnError(_this.xhr);
32542         }
32543         
32544         var formData = new FormData();
32545
32546         formData.append('returnHTML', 'NO');
32547         
32548         formData.append('crop', crop);
32549         
32550         if(typeof(file.filename) != 'undefined'){
32551             formData.append('filename', file.filename);
32552         }
32553         
32554         if(typeof(file.mimetype) != 'undefined'){
32555             formData.append('mimetype', file.mimetype);
32556         }
32557         
32558         Roo.log(formData);
32559         
32560         if(this.fireEvent('prepare', this, formData) != false){
32561             this.xhr.send(formData);
32562         };
32563     }
32564 });
32565
32566 /*
32567 * Licence: LGPL
32568 */
32569
32570 /**
32571  * @class Roo.bootstrap.DocumentViewer
32572  * @extends Roo.bootstrap.Component
32573  * Bootstrap DocumentViewer class
32574  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32575  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32576  * 
32577  * @constructor
32578  * Create a new DocumentViewer
32579  * @param {Object} config The config object
32580  */
32581
32582 Roo.bootstrap.DocumentViewer = function(config){
32583     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32584     
32585     this.addEvents({
32586         /**
32587          * @event initial
32588          * Fire after initEvent
32589          * @param {Roo.bootstrap.DocumentViewer} this
32590          */
32591         "initial" : true,
32592         /**
32593          * @event click
32594          * Fire after click
32595          * @param {Roo.bootstrap.DocumentViewer} this
32596          */
32597         "click" : true,
32598         /**
32599          * @event download
32600          * Fire after download button
32601          * @param {Roo.bootstrap.DocumentViewer} this
32602          */
32603         "download" : true,
32604         /**
32605          * @event trash
32606          * Fire after trash button
32607          * @param {Roo.bootstrap.DocumentViewer} this
32608          */
32609         "trash" : true
32610         
32611     });
32612 };
32613
32614 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32615     
32616     showDownload : true,
32617     
32618     showTrash : true,
32619     
32620     getAutoCreate : function()
32621     {
32622         var cfg = {
32623             tag : 'div',
32624             cls : 'roo-document-viewer',
32625             cn : [
32626                 {
32627                     tag : 'div',
32628                     cls : 'roo-document-viewer-body',
32629                     cn : [
32630                         {
32631                             tag : 'div',
32632                             cls : 'roo-document-viewer-thumb',
32633                             cn : [
32634                                 {
32635                                     tag : 'img',
32636                                     cls : 'roo-document-viewer-image'
32637                                 }
32638                             ]
32639                         }
32640                     ]
32641                 },
32642                 {
32643                     tag : 'div',
32644                     cls : 'roo-document-viewer-footer',
32645                     cn : {
32646                         tag : 'div',
32647                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32648                         cn : [
32649                             {
32650                                 tag : 'div',
32651                                 cls : 'btn-group roo-document-viewer-download',
32652                                 cn : [
32653                                     {
32654                                         tag : 'button',
32655                                         cls : 'btn btn-default',
32656                                         html : '<i class="fa fa-download"></i>'
32657                                     }
32658                                 ]
32659                             },
32660                             {
32661                                 tag : 'div',
32662                                 cls : 'btn-group roo-document-viewer-trash',
32663                                 cn : [
32664                                     {
32665                                         tag : 'button',
32666                                         cls : 'btn btn-default',
32667                                         html : '<i class="fa fa-trash"></i>'
32668                                     }
32669                                 ]
32670                             }
32671                         ]
32672                     }
32673                 }
32674             ]
32675         };
32676         
32677         return cfg;
32678     },
32679     
32680     initEvents : function()
32681     {
32682         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32683         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32684         
32685         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32686         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32687         
32688         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32689         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32690         
32691         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32692         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32693         
32694         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32695         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32696         
32697         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32698         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32699         
32700         this.bodyEl.on('click', this.onClick, this);
32701         this.downloadBtn.on('click', this.onDownload, this);
32702         this.trashBtn.on('click', this.onTrash, this);
32703         
32704         this.downloadBtn.hide();
32705         this.trashBtn.hide();
32706         
32707         if(this.showDownload){
32708             this.downloadBtn.show();
32709         }
32710         
32711         if(this.showTrash){
32712             this.trashBtn.show();
32713         }
32714         
32715         if(!this.showDownload && !this.showTrash) {
32716             this.footerEl.hide();
32717         }
32718         
32719     },
32720     
32721     initial : function()
32722     {
32723         this.fireEvent('initial', this);
32724         
32725     },
32726     
32727     onClick : function(e)
32728     {
32729         e.preventDefault();
32730         
32731         this.fireEvent('click', this);
32732     },
32733     
32734     onDownload : function(e)
32735     {
32736         e.preventDefault();
32737         
32738         this.fireEvent('download', this);
32739     },
32740     
32741     onTrash : function(e)
32742     {
32743         e.preventDefault();
32744         
32745         this.fireEvent('trash', this);
32746     }
32747     
32748 });
32749 /*
32750  * - LGPL
32751  *
32752  * nav progress bar
32753  * 
32754  */
32755
32756 /**
32757  * @class Roo.bootstrap.NavProgressBar
32758  * @extends Roo.bootstrap.Component
32759  * Bootstrap NavProgressBar class
32760  * 
32761  * @constructor
32762  * Create a new nav progress bar
32763  * @param {Object} config The config object
32764  */
32765
32766 Roo.bootstrap.NavProgressBar = function(config){
32767     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32768
32769     this.bullets = this.bullets || [];
32770    
32771 //    Roo.bootstrap.NavProgressBar.register(this);
32772      this.addEvents({
32773         /**
32774              * @event changed
32775              * Fires when the active item changes
32776              * @param {Roo.bootstrap.NavProgressBar} this
32777              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32778              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32779          */
32780         'changed': true
32781      });
32782     
32783 };
32784
32785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32786     
32787     bullets : [],
32788     barItems : [],
32789     
32790     getAutoCreate : function()
32791     {
32792         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32793         
32794         cfg = {
32795             tag : 'div',
32796             cls : 'roo-navigation-bar-group',
32797             cn : [
32798                 {
32799                     tag : 'div',
32800                     cls : 'roo-navigation-top-bar'
32801                 },
32802                 {
32803                     tag : 'div',
32804                     cls : 'roo-navigation-bullets-bar',
32805                     cn : [
32806                         {
32807                             tag : 'ul',
32808                             cls : 'roo-navigation-bar'
32809                         }
32810                     ]
32811                 },
32812                 
32813                 {
32814                     tag : 'div',
32815                     cls : 'roo-navigation-bottom-bar'
32816                 }
32817             ]
32818             
32819         };
32820         
32821         return cfg;
32822         
32823     },
32824     
32825     initEvents: function() 
32826     {
32827         
32828     },
32829     
32830     onRender : function(ct, position) 
32831     {
32832         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32833         
32834         if(this.bullets.length){
32835             Roo.each(this.bullets, function(b){
32836                this.addItem(b);
32837             }, this);
32838         }
32839         
32840         this.format();
32841         
32842     },
32843     
32844     addItem : function(cfg)
32845     {
32846         var item = new Roo.bootstrap.NavProgressItem(cfg);
32847         
32848         item.parentId = this.id;
32849         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32850         
32851         if(cfg.html){
32852             var top = new Roo.bootstrap.Element({
32853                 tag : 'div',
32854                 cls : 'roo-navigation-bar-text'
32855             });
32856             
32857             var bottom = new Roo.bootstrap.Element({
32858                 tag : 'div',
32859                 cls : 'roo-navigation-bar-text'
32860             });
32861             
32862             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32863             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32864             
32865             var topText = new Roo.bootstrap.Element({
32866                 tag : 'span',
32867                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32868             });
32869             
32870             var bottomText = new Roo.bootstrap.Element({
32871                 tag : 'span',
32872                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32873             });
32874             
32875             topText.onRender(top.el, null);
32876             bottomText.onRender(bottom.el, null);
32877             
32878             item.topEl = top;
32879             item.bottomEl = bottom;
32880         }
32881         
32882         this.barItems.push(item);
32883         
32884         return item;
32885     },
32886     
32887     getActive : function()
32888     {
32889         var active = false;
32890         
32891         Roo.each(this.barItems, function(v){
32892             
32893             if (!v.isActive()) {
32894                 return;
32895             }
32896             
32897             active = v;
32898             return false;
32899             
32900         });
32901         
32902         return active;
32903     },
32904     
32905     setActiveItem : function(item)
32906     {
32907         var prev = false;
32908         
32909         Roo.each(this.barItems, function(v){
32910             if (v.rid == item.rid) {
32911                 return ;
32912             }
32913             
32914             if (v.isActive()) {
32915                 v.setActive(false);
32916                 prev = v;
32917             }
32918         });
32919
32920         item.setActive(true);
32921         
32922         this.fireEvent('changed', this, item, prev);
32923     },
32924     
32925     getBarItem: function(rid)
32926     {
32927         var ret = false;
32928         
32929         Roo.each(this.barItems, function(e) {
32930             if (e.rid != rid) {
32931                 return;
32932             }
32933             
32934             ret =  e;
32935             return false;
32936         });
32937         
32938         return ret;
32939     },
32940     
32941     indexOfItem : function(item)
32942     {
32943         var index = false;
32944         
32945         Roo.each(this.barItems, function(v, i){
32946             
32947             if (v.rid != item.rid) {
32948                 return;
32949             }
32950             
32951             index = i;
32952             return false
32953         });
32954         
32955         return index;
32956     },
32957     
32958     setActiveNext : function()
32959     {
32960         var i = this.indexOfItem(this.getActive());
32961         
32962         if (i > this.barItems.length) {
32963             return;
32964         }
32965         
32966         this.setActiveItem(this.barItems[i+1]);
32967     },
32968     
32969     setActivePrev : function()
32970     {
32971         var i = this.indexOfItem(this.getActive());
32972         
32973         if (i  < 1) {
32974             return;
32975         }
32976         
32977         this.setActiveItem(this.barItems[i-1]);
32978     },
32979     
32980     format : function()
32981     {
32982         if(!this.barItems.length){
32983             return;
32984         }
32985      
32986         var width = 100 / this.barItems.length;
32987         
32988         Roo.each(this.barItems, function(i){
32989             i.el.setStyle('width', width + '%');
32990             i.topEl.el.setStyle('width', width + '%');
32991             i.bottomEl.el.setStyle('width', width + '%');
32992         }, this);
32993         
32994     }
32995     
32996 });
32997 /*
32998  * - LGPL
32999  *
33000  * Nav Progress Item
33001  * 
33002  */
33003
33004 /**
33005  * @class Roo.bootstrap.NavProgressItem
33006  * @extends Roo.bootstrap.Component
33007  * Bootstrap NavProgressItem class
33008  * @cfg {String} rid the reference id
33009  * @cfg {Boolean} active (true|false) Is item active default false
33010  * @cfg {Boolean} disabled (true|false) Is item active default false
33011  * @cfg {String} html
33012  * @cfg {String} position (top|bottom) text position default bottom
33013  * @cfg {String} icon show icon instead of number
33014  * 
33015  * @constructor
33016  * Create a new NavProgressItem
33017  * @param {Object} config The config object
33018  */
33019 Roo.bootstrap.NavProgressItem = function(config){
33020     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33021     this.addEvents({
33022         // raw events
33023         /**
33024          * @event click
33025          * The raw click event for the entire grid.
33026          * @param {Roo.bootstrap.NavProgressItem} this
33027          * @param {Roo.EventObject} e
33028          */
33029         "click" : true
33030     });
33031    
33032 };
33033
33034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33035     
33036     rid : '',
33037     active : false,
33038     disabled : false,
33039     html : '',
33040     position : 'bottom',
33041     icon : false,
33042     
33043     getAutoCreate : function()
33044     {
33045         var iconCls = 'roo-navigation-bar-item-icon';
33046         
33047         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33048         
33049         var cfg = {
33050             tag: 'li',
33051             cls: 'roo-navigation-bar-item',
33052             cn : [
33053                 {
33054                     tag : 'i',
33055                     cls : iconCls
33056                 }
33057             ]
33058         };
33059         
33060         if(this.active){
33061             cfg.cls += ' active';
33062         }
33063         if(this.disabled){
33064             cfg.cls += ' disabled';
33065         }
33066         
33067         return cfg;
33068     },
33069     
33070     disable : function()
33071     {
33072         this.setDisabled(true);
33073     },
33074     
33075     enable : function()
33076     {
33077         this.setDisabled(false);
33078     },
33079     
33080     initEvents: function() 
33081     {
33082         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33083         
33084         this.iconEl.on('click', this.onClick, this);
33085     },
33086     
33087     onClick : function(e)
33088     {
33089         e.preventDefault();
33090         
33091         if(this.disabled){
33092             return;
33093         }
33094         
33095         if(this.fireEvent('click', this, e) === false){
33096             return;
33097         };
33098         
33099         this.parent().setActiveItem(this);
33100     },
33101     
33102     isActive: function () 
33103     {
33104         return this.active;
33105     },
33106     
33107     setActive : function(state)
33108     {
33109         if(this.active == state){
33110             return;
33111         }
33112         
33113         this.active = state;
33114         
33115         if (state) {
33116             this.el.addClass('active');
33117             return;
33118         }
33119         
33120         this.el.removeClass('active');
33121         
33122         return;
33123     },
33124     
33125     setDisabled : function(state)
33126     {
33127         if(this.disabled == state){
33128             return;
33129         }
33130         
33131         this.disabled = state;
33132         
33133         if (state) {
33134             this.el.addClass('disabled');
33135             return;
33136         }
33137         
33138         this.el.removeClass('disabled');
33139     },
33140     
33141     tooltipEl : function()
33142     {
33143         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33144     }
33145 });
33146  
33147
33148  /*
33149  * - LGPL
33150  *
33151  * FieldLabel
33152  * 
33153  */
33154
33155 /**
33156  * @class Roo.bootstrap.FieldLabel
33157  * @extends Roo.bootstrap.Component
33158  * Bootstrap FieldLabel class
33159  * @cfg {String} html contents of the element
33160  * @cfg {String} tag tag of the element default label
33161  * @cfg {String} cls class of the element
33162  * @cfg {String} target label target 
33163  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33164  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33165  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33166  * @cfg {String} iconTooltip default "This field is required"
33167  * @cfg {String} indicatorpos (left|right) default left
33168  * 
33169  * @constructor
33170  * Create a new FieldLabel
33171  * @param {Object} config The config object
33172  */
33173
33174 Roo.bootstrap.FieldLabel = function(config){
33175     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33176     
33177     this.addEvents({
33178             /**
33179              * @event invalid
33180              * Fires after the field has been marked as invalid.
33181              * @param {Roo.form.FieldLabel} this
33182              * @param {String} msg The validation message
33183              */
33184             invalid : true,
33185             /**
33186              * @event valid
33187              * Fires after the field has been validated with no errors.
33188              * @param {Roo.form.FieldLabel} this
33189              */
33190             valid : true
33191         });
33192 };
33193
33194 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33195     
33196     tag: 'label',
33197     cls: '',
33198     html: '',
33199     target: '',
33200     allowBlank : true,
33201     invalidClass : 'has-warning',
33202     validClass : 'has-success',
33203     iconTooltip : 'This field is required',
33204     indicatorpos : 'left',
33205     
33206     getAutoCreate : function(){
33207         
33208         var cls = "";
33209         if (!this.allowBlank) {
33210             cls  = "visible";
33211         }
33212         
33213         var cfg = {
33214             tag : this.tag,
33215             cls : 'roo-bootstrap-field-label ' + this.cls,
33216             for : this.target,
33217             cn : [
33218                 {
33219                     tag : 'i',
33220                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33221                     tooltip : this.iconTooltip
33222                 },
33223                 {
33224                     tag : 'span',
33225                     html : this.html
33226                 }
33227             ] 
33228         };
33229         
33230         if(this.indicatorpos == 'right'){
33231             var cfg = {
33232                 tag : this.tag,
33233                 cls : 'roo-bootstrap-field-label ' + this.cls,
33234                 for : this.target,
33235                 cn : [
33236                     {
33237                         tag : 'span',
33238                         html : this.html
33239                     },
33240                     {
33241                         tag : 'i',
33242                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33243                         tooltip : this.iconTooltip
33244                     }
33245                 ] 
33246             };
33247         }
33248         
33249         return cfg;
33250     },
33251     
33252     initEvents: function() 
33253     {
33254         Roo.bootstrap.Element.superclass.initEvents.call(this);
33255         
33256         this.indicator = this.indicatorEl();
33257         
33258         if(this.indicator){
33259             this.indicator.removeClass('visible');
33260             this.indicator.addClass('invisible');
33261         }
33262         
33263         Roo.bootstrap.FieldLabel.register(this);
33264     },
33265     
33266     indicatorEl : function()
33267     {
33268         var indicator = this.el.select('i.roo-required-indicator',true).first();
33269         
33270         if(!indicator){
33271             return false;
33272         }
33273         
33274         return indicator;
33275         
33276     },
33277     
33278     /**
33279      * Mark this field as valid
33280      */
33281     markValid : function()
33282     {
33283         if(this.indicator){
33284             this.indicator.removeClass('visible');
33285             this.indicator.addClass('invisible');
33286         }
33287         if (Roo.bootstrap.version == 3) {
33288             this.el.removeClass(this.invalidClass);
33289             this.el.addClass(this.validClass);
33290         } else {
33291             this.el.removeClass('is-invalid');
33292             this.el.addClass('is-valid');
33293         }
33294         
33295         
33296         this.fireEvent('valid', this);
33297     },
33298     
33299     /**
33300      * Mark this field as invalid
33301      * @param {String} msg The validation message
33302      */
33303     markInvalid : function(msg)
33304     {
33305         if(this.indicator){
33306             this.indicator.removeClass('invisible');
33307             this.indicator.addClass('visible');
33308         }
33309           if (Roo.bootstrap.version == 3) {
33310             this.el.removeClass(this.validClass);
33311             this.el.addClass(this.invalidClass);
33312         } else {
33313             this.el.removeClass('is-valid');
33314             this.el.addClass('is-invalid');
33315         }
33316         
33317         
33318         this.fireEvent('invalid', this, msg);
33319     }
33320     
33321    
33322 });
33323
33324 Roo.apply(Roo.bootstrap.FieldLabel, {
33325     
33326     groups: {},
33327     
33328      /**
33329     * register a FieldLabel Group
33330     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33331     */
33332     register : function(label)
33333     {
33334         if(this.groups.hasOwnProperty(label.target)){
33335             return;
33336         }
33337      
33338         this.groups[label.target] = label;
33339         
33340     },
33341     /**
33342     * fetch a FieldLabel Group based on the target
33343     * @param {string} target
33344     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33345     */
33346     get: function(target) {
33347         if (typeof(this.groups[target]) == 'undefined') {
33348             return false;
33349         }
33350         
33351         return this.groups[target] ;
33352     }
33353 });
33354
33355  
33356
33357  /*
33358  * - LGPL
33359  *
33360  * page DateSplitField.
33361  * 
33362  */
33363
33364
33365 /**
33366  * @class Roo.bootstrap.DateSplitField
33367  * @extends Roo.bootstrap.Component
33368  * Bootstrap DateSplitField class
33369  * @cfg {string} fieldLabel - the label associated
33370  * @cfg {Number} labelWidth set the width of label (0-12)
33371  * @cfg {String} labelAlign (top|left)
33372  * @cfg {Boolean} dayAllowBlank (true|false) default false
33373  * @cfg {Boolean} monthAllowBlank (true|false) default false
33374  * @cfg {Boolean} yearAllowBlank (true|false) default false
33375  * @cfg {string} dayPlaceholder 
33376  * @cfg {string} monthPlaceholder
33377  * @cfg {string} yearPlaceholder
33378  * @cfg {string} dayFormat default 'd'
33379  * @cfg {string} monthFormat default 'm'
33380  * @cfg {string} yearFormat default 'Y'
33381  * @cfg {Number} labellg set the width of label (1-12)
33382  * @cfg {Number} labelmd set the width of label (1-12)
33383  * @cfg {Number} labelsm set the width of label (1-12)
33384  * @cfg {Number} labelxs set the width of label (1-12)
33385
33386  *     
33387  * @constructor
33388  * Create a new DateSplitField
33389  * @param {Object} config The config object
33390  */
33391
33392 Roo.bootstrap.DateSplitField = function(config){
33393     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33394     
33395     this.addEvents({
33396         // raw events
33397          /**
33398          * @event years
33399          * getting the data of years
33400          * @param {Roo.bootstrap.DateSplitField} this
33401          * @param {Object} years
33402          */
33403         "years" : true,
33404         /**
33405          * @event days
33406          * getting the data of days
33407          * @param {Roo.bootstrap.DateSplitField} this
33408          * @param {Object} days
33409          */
33410         "days" : true,
33411         /**
33412          * @event invalid
33413          * Fires after the field has been marked as invalid.
33414          * @param {Roo.form.Field} this
33415          * @param {String} msg The validation message
33416          */
33417         invalid : true,
33418        /**
33419          * @event valid
33420          * Fires after the field has been validated with no errors.
33421          * @param {Roo.form.Field} this
33422          */
33423         valid : true
33424     });
33425 };
33426
33427 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33428     
33429     fieldLabel : '',
33430     labelAlign : 'top',
33431     labelWidth : 3,
33432     dayAllowBlank : false,
33433     monthAllowBlank : false,
33434     yearAllowBlank : false,
33435     dayPlaceholder : '',
33436     monthPlaceholder : '',
33437     yearPlaceholder : '',
33438     dayFormat : 'd',
33439     monthFormat : 'm',
33440     yearFormat : 'Y',
33441     isFormField : true,
33442     labellg : 0,
33443     labelmd : 0,
33444     labelsm : 0,
33445     labelxs : 0,
33446     
33447     getAutoCreate : function()
33448     {
33449         var cfg = {
33450             tag : 'div',
33451             cls : 'row roo-date-split-field-group',
33452             cn : [
33453                 {
33454                     tag : 'input',
33455                     type : 'hidden',
33456                     cls : 'form-hidden-field roo-date-split-field-group-value',
33457                     name : this.name
33458                 }
33459             ]
33460         };
33461         
33462         var labelCls = 'col-md-12';
33463         var contentCls = 'col-md-4';
33464         
33465         if(this.fieldLabel){
33466             
33467             var label = {
33468                 tag : 'div',
33469                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33470                 cn : [
33471                     {
33472                         tag : 'label',
33473                         html : this.fieldLabel
33474                     }
33475                 ]
33476             };
33477             
33478             if(this.labelAlign == 'left'){
33479             
33480                 if(this.labelWidth > 12){
33481                     label.style = "width: " + this.labelWidth + 'px';
33482                 }
33483
33484                 if(this.labelWidth < 13 && this.labelmd == 0){
33485                     this.labelmd = this.labelWidth;
33486                 }
33487
33488                 if(this.labellg > 0){
33489                     labelCls = ' col-lg-' + this.labellg;
33490                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33491                 }
33492
33493                 if(this.labelmd > 0){
33494                     labelCls = ' col-md-' + this.labelmd;
33495                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33496                 }
33497
33498                 if(this.labelsm > 0){
33499                     labelCls = ' col-sm-' + this.labelsm;
33500                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33501                 }
33502
33503                 if(this.labelxs > 0){
33504                     labelCls = ' col-xs-' + this.labelxs;
33505                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33506                 }
33507             }
33508             
33509             label.cls += ' ' + labelCls;
33510             
33511             cfg.cn.push(label);
33512         }
33513         
33514         Roo.each(['day', 'month', 'year'], function(t){
33515             cfg.cn.push({
33516                 tag : 'div',
33517                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33518             });
33519         }, this);
33520         
33521         return cfg;
33522     },
33523     
33524     inputEl: function ()
33525     {
33526         return this.el.select('.roo-date-split-field-group-value', true).first();
33527     },
33528     
33529     onRender : function(ct, position) 
33530     {
33531         var _this = this;
33532         
33533         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33534         
33535         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33536         
33537         this.dayField = new Roo.bootstrap.ComboBox({
33538             allowBlank : this.dayAllowBlank,
33539             alwaysQuery : true,
33540             displayField : 'value',
33541             editable : false,
33542             fieldLabel : '',
33543             forceSelection : true,
33544             mode : 'local',
33545             placeholder : this.dayPlaceholder,
33546             selectOnFocus : true,
33547             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33548             triggerAction : 'all',
33549             typeAhead : true,
33550             valueField : 'value',
33551             store : new Roo.data.SimpleStore({
33552                 data : (function() {    
33553                     var days = [];
33554                     _this.fireEvent('days', _this, days);
33555                     return days;
33556                 })(),
33557                 fields : [ 'value' ]
33558             }),
33559             listeners : {
33560                 select : function (_self, record, index)
33561                 {
33562                     _this.setValue(_this.getValue());
33563                 }
33564             }
33565         });
33566
33567         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33568         
33569         this.monthField = new Roo.bootstrap.MonthField({
33570             after : '<i class=\"fa fa-calendar\"></i>',
33571             allowBlank : this.monthAllowBlank,
33572             placeholder : this.monthPlaceholder,
33573             readOnly : true,
33574             listeners : {
33575                 render : function (_self)
33576                 {
33577                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33578                         e.preventDefault();
33579                         _self.focus();
33580                     });
33581                 },
33582                 select : function (_self, oldvalue, newvalue)
33583                 {
33584                     _this.setValue(_this.getValue());
33585                 }
33586             }
33587         });
33588         
33589         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33590         
33591         this.yearField = new Roo.bootstrap.ComboBox({
33592             allowBlank : this.yearAllowBlank,
33593             alwaysQuery : true,
33594             displayField : 'value',
33595             editable : false,
33596             fieldLabel : '',
33597             forceSelection : true,
33598             mode : 'local',
33599             placeholder : this.yearPlaceholder,
33600             selectOnFocus : true,
33601             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33602             triggerAction : 'all',
33603             typeAhead : true,
33604             valueField : 'value',
33605             store : new Roo.data.SimpleStore({
33606                 data : (function() {
33607                     var years = [];
33608                     _this.fireEvent('years', _this, years);
33609                     return years;
33610                 })(),
33611                 fields : [ 'value' ]
33612             }),
33613             listeners : {
33614                 select : function (_self, record, index)
33615                 {
33616                     _this.setValue(_this.getValue());
33617                 }
33618             }
33619         });
33620
33621         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33622     },
33623     
33624     setValue : function(v, format)
33625     {
33626         this.inputEl.dom.value = v;
33627         
33628         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33629         
33630         var d = Date.parseDate(v, f);
33631         
33632         if(!d){
33633             this.validate();
33634             return;
33635         }
33636         
33637         this.setDay(d.format(this.dayFormat));
33638         this.setMonth(d.format(this.monthFormat));
33639         this.setYear(d.format(this.yearFormat));
33640         
33641         this.validate();
33642         
33643         return;
33644     },
33645     
33646     setDay : function(v)
33647     {
33648         this.dayField.setValue(v);
33649         this.inputEl.dom.value = this.getValue();
33650         this.validate();
33651         return;
33652     },
33653     
33654     setMonth : function(v)
33655     {
33656         this.monthField.setValue(v, true);
33657         this.inputEl.dom.value = this.getValue();
33658         this.validate();
33659         return;
33660     },
33661     
33662     setYear : function(v)
33663     {
33664         this.yearField.setValue(v);
33665         this.inputEl.dom.value = this.getValue();
33666         this.validate();
33667         return;
33668     },
33669     
33670     getDay : function()
33671     {
33672         return this.dayField.getValue();
33673     },
33674     
33675     getMonth : function()
33676     {
33677         return this.monthField.getValue();
33678     },
33679     
33680     getYear : function()
33681     {
33682         return this.yearField.getValue();
33683     },
33684     
33685     getValue : function()
33686     {
33687         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33688         
33689         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33690         
33691         return date;
33692     },
33693     
33694     reset : function()
33695     {
33696         this.setDay('');
33697         this.setMonth('');
33698         this.setYear('');
33699         this.inputEl.dom.value = '';
33700         this.validate();
33701         return;
33702     },
33703     
33704     validate : function()
33705     {
33706         var d = this.dayField.validate();
33707         var m = this.monthField.validate();
33708         var y = this.yearField.validate();
33709         
33710         var valid = true;
33711         
33712         if(
33713                 (!this.dayAllowBlank && !d) ||
33714                 (!this.monthAllowBlank && !m) ||
33715                 (!this.yearAllowBlank && !y)
33716         ){
33717             valid = false;
33718         }
33719         
33720         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33721             return valid;
33722         }
33723         
33724         if(valid){
33725             this.markValid();
33726             return valid;
33727         }
33728         
33729         this.markInvalid();
33730         
33731         return valid;
33732     },
33733     
33734     markValid : function()
33735     {
33736         
33737         var label = this.el.select('label', true).first();
33738         var icon = this.el.select('i.fa-star', true).first();
33739
33740         if(label && icon){
33741             icon.remove();
33742         }
33743         
33744         this.fireEvent('valid', this);
33745     },
33746     
33747      /**
33748      * Mark this field as invalid
33749      * @param {String} msg The validation message
33750      */
33751     markInvalid : function(msg)
33752     {
33753         
33754         var label = this.el.select('label', true).first();
33755         var icon = this.el.select('i.fa-star', true).first();
33756
33757         if(label && !icon){
33758             this.el.select('.roo-date-split-field-label', true).createChild({
33759                 tag : 'i',
33760                 cls : 'text-danger fa fa-lg fa-star',
33761                 tooltip : 'This field is required',
33762                 style : 'margin-right:5px;'
33763             }, label, true);
33764         }
33765         
33766         this.fireEvent('invalid', this, msg);
33767     },
33768     
33769     clearInvalid : function()
33770     {
33771         var label = this.el.select('label', true).first();
33772         var icon = this.el.select('i.fa-star', true).first();
33773
33774         if(label && icon){
33775             icon.remove();
33776         }
33777         
33778         this.fireEvent('valid', this);
33779     },
33780     
33781     getName: function()
33782     {
33783         return this.name;
33784     }
33785     
33786 });
33787
33788  /**
33789  *
33790  * This is based on 
33791  * http://masonry.desandro.com
33792  *
33793  * The idea is to render all the bricks based on vertical width...
33794  *
33795  * The original code extends 'outlayer' - we might need to use that....
33796  * 
33797  */
33798
33799
33800 /**
33801  * @class Roo.bootstrap.LayoutMasonry
33802  * @extends Roo.bootstrap.Component
33803  * Bootstrap Layout Masonry class
33804  * 
33805  * @constructor
33806  * Create a new Element
33807  * @param {Object} config The config object
33808  */
33809
33810 Roo.bootstrap.LayoutMasonry = function(config){
33811     
33812     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33813     
33814     this.bricks = [];
33815     
33816     Roo.bootstrap.LayoutMasonry.register(this);
33817     
33818     this.addEvents({
33819         // raw events
33820         /**
33821          * @event layout
33822          * Fire after layout the items
33823          * @param {Roo.bootstrap.LayoutMasonry} this
33824          * @param {Roo.EventObject} e
33825          */
33826         "layout" : true
33827     });
33828     
33829 };
33830
33831 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33832     
33833     /**
33834      * @cfg {Boolean} isLayoutInstant = no animation?
33835      */   
33836     isLayoutInstant : false, // needed?
33837    
33838     /**
33839      * @cfg {Number} boxWidth  width of the columns
33840      */   
33841     boxWidth : 450,
33842     
33843       /**
33844      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33845      */   
33846     boxHeight : 0,
33847     
33848     /**
33849      * @cfg {Number} padWidth padding below box..
33850      */   
33851     padWidth : 10, 
33852     
33853     /**
33854      * @cfg {Number} gutter gutter width..
33855      */   
33856     gutter : 10,
33857     
33858      /**
33859      * @cfg {Number} maxCols maximum number of columns
33860      */   
33861     
33862     maxCols: 0,
33863     
33864     /**
33865      * @cfg {Boolean} isAutoInitial defalut true
33866      */   
33867     isAutoInitial : true, 
33868     
33869     containerWidth: 0,
33870     
33871     /**
33872      * @cfg {Boolean} isHorizontal defalut false
33873      */   
33874     isHorizontal : false, 
33875
33876     currentSize : null,
33877     
33878     tag: 'div',
33879     
33880     cls: '',
33881     
33882     bricks: null, //CompositeElement
33883     
33884     cols : 1,
33885     
33886     _isLayoutInited : false,
33887     
33888 //    isAlternative : false, // only use for vertical layout...
33889     
33890     /**
33891      * @cfg {Number} alternativePadWidth padding below box..
33892      */   
33893     alternativePadWidth : 50,
33894     
33895     selectedBrick : [],
33896     
33897     getAutoCreate : function(){
33898         
33899         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33900         
33901         var cfg = {
33902             tag: this.tag,
33903             cls: 'blog-masonary-wrapper ' + this.cls,
33904             cn : {
33905                 cls : 'mas-boxes masonary'
33906             }
33907         };
33908         
33909         return cfg;
33910     },
33911     
33912     getChildContainer: function( )
33913     {
33914         if (this.boxesEl) {
33915             return this.boxesEl;
33916         }
33917         
33918         this.boxesEl = this.el.select('.mas-boxes').first();
33919         
33920         return this.boxesEl;
33921     },
33922     
33923     
33924     initEvents : function()
33925     {
33926         var _this = this;
33927         
33928         if(this.isAutoInitial){
33929             Roo.log('hook children rendered');
33930             this.on('childrenrendered', function() {
33931                 Roo.log('children rendered');
33932                 _this.initial();
33933             } ,this);
33934         }
33935     },
33936     
33937     initial : function()
33938     {
33939         this.selectedBrick = [];
33940         
33941         this.currentSize = this.el.getBox(true);
33942         
33943         Roo.EventManager.onWindowResize(this.resize, this); 
33944
33945         if(!this.isAutoInitial){
33946             this.layout();
33947             return;
33948         }
33949         
33950         this.layout();
33951         
33952         return;
33953         //this.layout.defer(500,this);
33954         
33955     },
33956     
33957     resize : function()
33958     {
33959         var cs = this.el.getBox(true);
33960         
33961         if (
33962                 this.currentSize.width == cs.width && 
33963                 this.currentSize.x == cs.x && 
33964                 this.currentSize.height == cs.height && 
33965                 this.currentSize.y == cs.y 
33966         ) {
33967             Roo.log("no change in with or X or Y");
33968             return;
33969         }
33970         
33971         this.currentSize = cs;
33972         
33973         this.layout();
33974         
33975     },
33976     
33977     layout : function()
33978     {   
33979         this._resetLayout();
33980         
33981         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33982         
33983         this.layoutItems( isInstant );
33984       
33985         this._isLayoutInited = true;
33986         
33987         this.fireEvent('layout', this);
33988         
33989     },
33990     
33991     _resetLayout : function()
33992     {
33993         if(this.isHorizontal){
33994             this.horizontalMeasureColumns();
33995             return;
33996         }
33997         
33998         this.verticalMeasureColumns();
33999         
34000     },
34001     
34002     verticalMeasureColumns : function()
34003     {
34004         this.getContainerWidth();
34005         
34006 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34007 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34008 //            return;
34009 //        }
34010         
34011         var boxWidth = this.boxWidth + this.padWidth;
34012         
34013         if(this.containerWidth < this.boxWidth){
34014             boxWidth = this.containerWidth
34015         }
34016         
34017         var containerWidth = this.containerWidth;
34018         
34019         var cols = Math.floor(containerWidth / boxWidth);
34020         
34021         this.cols = Math.max( cols, 1 );
34022         
34023         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34024         
34025         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34026         
34027         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34028         
34029         this.colWidth = boxWidth + avail - this.padWidth;
34030         
34031         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34032         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34033     },
34034     
34035     horizontalMeasureColumns : function()
34036     {
34037         this.getContainerWidth();
34038         
34039         var boxWidth = this.boxWidth;
34040         
34041         if(this.containerWidth < boxWidth){
34042             boxWidth = this.containerWidth;
34043         }
34044         
34045         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34046         
34047         this.el.setHeight(boxWidth);
34048         
34049     },
34050     
34051     getContainerWidth : function()
34052     {
34053         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34054     },
34055     
34056     layoutItems : function( isInstant )
34057     {
34058         Roo.log(this.bricks);
34059         
34060         var items = Roo.apply([], this.bricks);
34061         
34062         if(this.isHorizontal){
34063             this._horizontalLayoutItems( items , isInstant );
34064             return;
34065         }
34066         
34067 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34068 //            this._verticalAlternativeLayoutItems( items , isInstant );
34069 //            return;
34070 //        }
34071         
34072         this._verticalLayoutItems( items , isInstant );
34073         
34074     },
34075     
34076     _verticalLayoutItems : function ( items , isInstant)
34077     {
34078         if ( !items || !items.length ) {
34079             return;
34080         }
34081         
34082         var standard = [
34083             ['xs', 'xs', 'xs', 'tall'],
34084             ['xs', 'xs', 'tall'],
34085             ['xs', 'xs', 'sm'],
34086             ['xs', 'xs', 'xs'],
34087             ['xs', 'tall'],
34088             ['xs', 'sm'],
34089             ['xs', 'xs'],
34090             ['xs'],
34091             
34092             ['sm', 'xs', 'xs'],
34093             ['sm', 'xs'],
34094             ['sm'],
34095             
34096             ['tall', 'xs', 'xs', 'xs'],
34097             ['tall', 'xs', 'xs'],
34098             ['tall', 'xs'],
34099             ['tall']
34100             
34101         ];
34102         
34103         var queue = [];
34104         
34105         var boxes = [];
34106         
34107         var box = [];
34108         
34109         Roo.each(items, function(item, k){
34110             
34111             switch (item.size) {
34112                 // these layouts take up a full box,
34113                 case 'md' :
34114                 case 'md-left' :
34115                 case 'md-right' :
34116                 case 'wide' :
34117                     
34118                     if(box.length){
34119                         boxes.push(box);
34120                         box = [];
34121                     }
34122                     
34123                     boxes.push([item]);
34124                     
34125                     break;
34126                     
34127                 case 'xs' :
34128                 case 'sm' :
34129                 case 'tall' :
34130                     
34131                     box.push(item);
34132                     
34133                     break;
34134                 default :
34135                     break;
34136                     
34137             }
34138             
34139         }, this);
34140         
34141         if(box.length){
34142             boxes.push(box);
34143             box = [];
34144         }
34145         
34146         var filterPattern = function(box, length)
34147         {
34148             if(!box.length){
34149                 return;
34150             }
34151             
34152             var match = false;
34153             
34154             var pattern = box.slice(0, length);
34155             
34156             var format = [];
34157             
34158             Roo.each(pattern, function(i){
34159                 format.push(i.size);
34160             }, this);
34161             
34162             Roo.each(standard, function(s){
34163                 
34164                 if(String(s) != String(format)){
34165                     return;
34166                 }
34167                 
34168                 match = true;
34169                 return false;
34170                 
34171             }, this);
34172             
34173             if(!match && length == 1){
34174                 return;
34175             }
34176             
34177             if(!match){
34178                 filterPattern(box, length - 1);
34179                 return;
34180             }
34181                 
34182             queue.push(pattern);
34183
34184             box = box.slice(length, box.length);
34185
34186             filterPattern(box, 4);
34187
34188             return;
34189             
34190         }
34191         
34192         Roo.each(boxes, function(box, k){
34193             
34194             if(!box.length){
34195                 return;
34196             }
34197             
34198             if(box.length == 1){
34199                 queue.push(box);
34200                 return;
34201             }
34202             
34203             filterPattern(box, 4);
34204             
34205         }, this);
34206         
34207         this._processVerticalLayoutQueue( queue, isInstant );
34208         
34209     },
34210     
34211 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34212 //    {
34213 //        if ( !items || !items.length ) {
34214 //            return;
34215 //        }
34216 //
34217 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34218 //        
34219 //    },
34220     
34221     _horizontalLayoutItems : function ( items , isInstant)
34222     {
34223         if ( !items || !items.length || items.length < 3) {
34224             return;
34225         }
34226         
34227         items.reverse();
34228         
34229         var eItems = items.slice(0, 3);
34230         
34231         items = items.slice(3, items.length);
34232         
34233         var standard = [
34234             ['xs', 'xs', 'xs', 'wide'],
34235             ['xs', 'xs', 'wide'],
34236             ['xs', 'xs', 'sm'],
34237             ['xs', 'xs', 'xs'],
34238             ['xs', 'wide'],
34239             ['xs', 'sm'],
34240             ['xs', 'xs'],
34241             ['xs'],
34242             
34243             ['sm', 'xs', 'xs'],
34244             ['sm', 'xs'],
34245             ['sm'],
34246             
34247             ['wide', 'xs', 'xs', 'xs'],
34248             ['wide', 'xs', 'xs'],
34249             ['wide', 'xs'],
34250             ['wide'],
34251             
34252             ['wide-thin']
34253         ];
34254         
34255         var queue = [];
34256         
34257         var boxes = [];
34258         
34259         var box = [];
34260         
34261         Roo.each(items, function(item, k){
34262             
34263             switch (item.size) {
34264                 case 'md' :
34265                 case 'md-left' :
34266                 case 'md-right' :
34267                 case 'tall' :
34268                     
34269                     if(box.length){
34270                         boxes.push(box);
34271                         box = [];
34272                     }
34273                     
34274                     boxes.push([item]);
34275                     
34276                     break;
34277                     
34278                 case 'xs' :
34279                 case 'sm' :
34280                 case 'wide' :
34281                 case 'wide-thin' :
34282                     
34283                     box.push(item);
34284                     
34285                     break;
34286                 default :
34287                     break;
34288                     
34289             }
34290             
34291         }, this);
34292         
34293         if(box.length){
34294             boxes.push(box);
34295             box = [];
34296         }
34297         
34298         var filterPattern = function(box, length)
34299         {
34300             if(!box.length){
34301                 return;
34302             }
34303             
34304             var match = false;
34305             
34306             var pattern = box.slice(0, length);
34307             
34308             var format = [];
34309             
34310             Roo.each(pattern, function(i){
34311                 format.push(i.size);
34312             }, this);
34313             
34314             Roo.each(standard, function(s){
34315                 
34316                 if(String(s) != String(format)){
34317                     return;
34318                 }
34319                 
34320                 match = true;
34321                 return false;
34322                 
34323             }, this);
34324             
34325             if(!match && length == 1){
34326                 return;
34327             }
34328             
34329             if(!match){
34330                 filterPattern(box, length - 1);
34331                 return;
34332             }
34333                 
34334             queue.push(pattern);
34335
34336             box = box.slice(length, box.length);
34337
34338             filterPattern(box, 4);
34339
34340             return;
34341             
34342         }
34343         
34344         Roo.each(boxes, function(box, k){
34345             
34346             if(!box.length){
34347                 return;
34348             }
34349             
34350             if(box.length == 1){
34351                 queue.push(box);
34352                 return;
34353             }
34354             
34355             filterPattern(box, 4);
34356             
34357         }, this);
34358         
34359         
34360         var prune = [];
34361         
34362         var pos = this.el.getBox(true);
34363         
34364         var minX = pos.x;
34365         
34366         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34367         
34368         var hit_end = false;
34369         
34370         Roo.each(queue, function(box){
34371             
34372             if(hit_end){
34373                 
34374                 Roo.each(box, function(b){
34375                 
34376                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34377                     b.el.hide();
34378
34379                 }, this);
34380
34381                 return;
34382             }
34383             
34384             var mx = 0;
34385             
34386             Roo.each(box, function(b){
34387                 
34388                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34389                 b.el.show();
34390
34391                 mx = Math.max(mx, b.x);
34392                 
34393             }, this);
34394             
34395             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34396             
34397             if(maxX < minX){
34398                 
34399                 Roo.each(box, function(b){
34400                 
34401                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34402                     b.el.hide();
34403                     
34404                 }, this);
34405                 
34406                 hit_end = true;
34407                 
34408                 return;
34409             }
34410             
34411             prune.push(box);
34412             
34413         }, this);
34414         
34415         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34416     },
34417     
34418     /** Sets position of item in DOM
34419     * @param {Element} item
34420     * @param {Number} x - horizontal position
34421     * @param {Number} y - vertical position
34422     * @param {Boolean} isInstant - disables transitions
34423     */
34424     _processVerticalLayoutQueue : function( queue, isInstant )
34425     {
34426         var pos = this.el.getBox(true);
34427         var x = pos.x;
34428         var y = pos.y;
34429         var maxY = [];
34430         
34431         for (var i = 0; i < this.cols; i++){
34432             maxY[i] = pos.y;
34433         }
34434         
34435         Roo.each(queue, function(box, k){
34436             
34437             var col = k % this.cols;
34438             
34439             Roo.each(box, function(b,kk){
34440                 
34441                 b.el.position('absolute');
34442                 
34443                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34444                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34445                 
34446                 if(b.size == 'md-left' || b.size == 'md-right'){
34447                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34448                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34449                 }
34450                 
34451                 b.el.setWidth(width);
34452                 b.el.setHeight(height);
34453                 // iframe?
34454                 b.el.select('iframe',true).setSize(width,height);
34455                 
34456             }, this);
34457             
34458             for (var i = 0; i < this.cols; i++){
34459                 
34460                 if(maxY[i] < maxY[col]){
34461                     col = i;
34462                     continue;
34463                 }
34464                 
34465                 col = Math.min(col, i);
34466                 
34467             }
34468             
34469             x = pos.x + col * (this.colWidth + this.padWidth);
34470             
34471             y = maxY[col];
34472             
34473             var positions = [];
34474             
34475             switch (box.length){
34476                 case 1 :
34477                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34478                     break;
34479                 case 2 :
34480                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34481                     break;
34482                 case 3 :
34483                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34484                     break;
34485                 case 4 :
34486                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34487                     break;
34488                 default :
34489                     break;
34490             }
34491             
34492             Roo.each(box, function(b,kk){
34493                 
34494                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34495                 
34496                 var sz = b.el.getSize();
34497                 
34498                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34499                 
34500             }, this);
34501             
34502         }, this);
34503         
34504         var mY = 0;
34505         
34506         for (var i = 0; i < this.cols; i++){
34507             mY = Math.max(mY, maxY[i]);
34508         }
34509         
34510         this.el.setHeight(mY - pos.y);
34511         
34512     },
34513     
34514 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34515 //    {
34516 //        var pos = this.el.getBox(true);
34517 //        var x = pos.x;
34518 //        var y = pos.y;
34519 //        var maxX = pos.right;
34520 //        
34521 //        var maxHeight = 0;
34522 //        
34523 //        Roo.each(items, function(item, k){
34524 //            
34525 //            var c = k % 2;
34526 //            
34527 //            item.el.position('absolute');
34528 //                
34529 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34530 //
34531 //            item.el.setWidth(width);
34532 //
34533 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34534 //
34535 //            item.el.setHeight(height);
34536 //            
34537 //            if(c == 0){
34538 //                item.el.setXY([x, y], isInstant ? false : true);
34539 //            } else {
34540 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34541 //            }
34542 //            
34543 //            y = y + height + this.alternativePadWidth;
34544 //            
34545 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34546 //            
34547 //        }, this);
34548 //        
34549 //        this.el.setHeight(maxHeight);
34550 //        
34551 //    },
34552     
34553     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34554     {
34555         var pos = this.el.getBox(true);
34556         
34557         var minX = pos.x;
34558         var minY = pos.y;
34559         
34560         var maxX = pos.right;
34561         
34562         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34563         
34564         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34565         
34566         Roo.each(queue, function(box, k){
34567             
34568             Roo.each(box, function(b, kk){
34569                 
34570                 b.el.position('absolute');
34571                 
34572                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34573                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34574                 
34575                 if(b.size == 'md-left' || b.size == 'md-right'){
34576                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34577                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34578                 }
34579                 
34580                 b.el.setWidth(width);
34581                 b.el.setHeight(height);
34582                 
34583             }, this);
34584             
34585             if(!box.length){
34586                 return;
34587             }
34588             
34589             var positions = [];
34590             
34591             switch (box.length){
34592                 case 1 :
34593                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34594                     break;
34595                 case 2 :
34596                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34597                     break;
34598                 case 3 :
34599                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34600                     break;
34601                 case 4 :
34602                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34603                     break;
34604                 default :
34605                     break;
34606             }
34607             
34608             Roo.each(box, function(b,kk){
34609                 
34610                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34611                 
34612                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34613                 
34614             }, this);
34615             
34616         }, this);
34617         
34618     },
34619     
34620     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34621     {
34622         Roo.each(eItems, function(b,k){
34623             
34624             b.size = (k == 0) ? 'sm' : 'xs';
34625             b.x = (k == 0) ? 2 : 1;
34626             b.y = (k == 0) ? 2 : 1;
34627             
34628             b.el.position('absolute');
34629             
34630             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34631                 
34632             b.el.setWidth(width);
34633             
34634             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34635             
34636             b.el.setHeight(height);
34637             
34638         }, this);
34639
34640         var positions = [];
34641         
34642         positions.push({
34643             x : maxX - this.unitWidth * 2 - this.gutter,
34644             y : minY
34645         });
34646         
34647         positions.push({
34648             x : maxX - this.unitWidth,
34649             y : minY + (this.unitWidth + this.gutter) * 2
34650         });
34651         
34652         positions.push({
34653             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34654             y : minY
34655         });
34656         
34657         Roo.each(eItems, function(b,k){
34658             
34659             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34660
34661         }, this);
34662         
34663     },
34664     
34665     getVerticalOneBoxColPositions : function(x, y, box)
34666     {
34667         var pos = [];
34668         
34669         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34670         
34671         if(box[0].size == 'md-left'){
34672             rand = 0;
34673         }
34674         
34675         if(box[0].size == 'md-right'){
34676             rand = 1;
34677         }
34678         
34679         pos.push({
34680             x : x + (this.unitWidth + this.gutter) * rand,
34681             y : y
34682         });
34683         
34684         return pos;
34685     },
34686     
34687     getVerticalTwoBoxColPositions : function(x, y, box)
34688     {
34689         var pos = [];
34690         
34691         if(box[0].size == 'xs'){
34692             
34693             pos.push({
34694                 x : x,
34695                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34696             });
34697
34698             pos.push({
34699                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34700                 y : y
34701             });
34702             
34703             return pos;
34704             
34705         }
34706         
34707         pos.push({
34708             x : x,
34709             y : y
34710         });
34711
34712         pos.push({
34713             x : x + (this.unitWidth + this.gutter) * 2,
34714             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34715         });
34716         
34717         return pos;
34718         
34719     },
34720     
34721     getVerticalThreeBoxColPositions : function(x, y, box)
34722     {
34723         var pos = [];
34724         
34725         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34726             
34727             pos.push({
34728                 x : x,
34729                 y : y
34730             });
34731
34732             pos.push({
34733                 x : x + (this.unitWidth + this.gutter) * 1,
34734                 y : y
34735             });
34736             
34737             pos.push({
34738                 x : x + (this.unitWidth + this.gutter) * 2,
34739                 y : y
34740             });
34741             
34742             return pos;
34743             
34744         }
34745         
34746         if(box[0].size == 'xs' && box[1].size == 'xs'){
34747             
34748             pos.push({
34749                 x : x,
34750                 y : y
34751             });
34752
34753             pos.push({
34754                 x : x,
34755                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34756             });
34757             
34758             pos.push({
34759                 x : x + (this.unitWidth + this.gutter) * 1,
34760                 y : y
34761             });
34762             
34763             return pos;
34764             
34765         }
34766         
34767         pos.push({
34768             x : x,
34769             y : y
34770         });
34771
34772         pos.push({
34773             x : x + (this.unitWidth + this.gutter) * 2,
34774             y : y
34775         });
34776
34777         pos.push({
34778             x : x + (this.unitWidth + this.gutter) * 2,
34779             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34780         });
34781             
34782         return pos;
34783         
34784     },
34785     
34786     getVerticalFourBoxColPositions : function(x, y, box)
34787     {
34788         var pos = [];
34789         
34790         if(box[0].size == 'xs'){
34791             
34792             pos.push({
34793                 x : x,
34794                 y : y
34795             });
34796
34797             pos.push({
34798                 x : x,
34799                 y : y + (this.unitHeight + this.gutter) * 1
34800             });
34801             
34802             pos.push({
34803                 x : x,
34804                 y : y + (this.unitHeight + this.gutter) * 2
34805             });
34806             
34807             pos.push({
34808                 x : x + (this.unitWidth + this.gutter) * 1,
34809                 y : y
34810             });
34811             
34812             return pos;
34813             
34814         }
34815         
34816         pos.push({
34817             x : x,
34818             y : y
34819         });
34820
34821         pos.push({
34822             x : x + (this.unitWidth + this.gutter) * 2,
34823             y : y
34824         });
34825
34826         pos.push({
34827             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34828             y : y + (this.unitHeight + this.gutter) * 1
34829         });
34830
34831         pos.push({
34832             x : x + (this.unitWidth + this.gutter) * 2,
34833             y : y + (this.unitWidth + this.gutter) * 2
34834         });
34835
34836         return pos;
34837         
34838     },
34839     
34840     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34841     {
34842         var pos = [];
34843         
34844         if(box[0].size == 'md-left'){
34845             pos.push({
34846                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34847                 y : minY
34848             });
34849             
34850             return pos;
34851         }
34852         
34853         if(box[0].size == 'md-right'){
34854             pos.push({
34855                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34856                 y : minY + (this.unitWidth + this.gutter) * 1
34857             });
34858             
34859             return pos;
34860         }
34861         
34862         var rand = Math.floor(Math.random() * (4 - box[0].y));
34863         
34864         pos.push({
34865             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34866             y : minY + (this.unitWidth + this.gutter) * rand
34867         });
34868         
34869         return pos;
34870         
34871     },
34872     
34873     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34874     {
34875         var pos = [];
34876         
34877         if(box[0].size == 'xs'){
34878             
34879             pos.push({
34880                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34881                 y : minY
34882             });
34883
34884             pos.push({
34885                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34886                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34887             });
34888             
34889             return pos;
34890             
34891         }
34892         
34893         pos.push({
34894             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34895             y : minY
34896         });
34897
34898         pos.push({
34899             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34900             y : minY + (this.unitWidth + this.gutter) * 2
34901         });
34902         
34903         return pos;
34904         
34905     },
34906     
34907     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34908     {
34909         var pos = [];
34910         
34911         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34912             
34913             pos.push({
34914                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34915                 y : minY
34916             });
34917
34918             pos.push({
34919                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34920                 y : minY + (this.unitWidth + this.gutter) * 1
34921             });
34922             
34923             pos.push({
34924                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34925                 y : minY + (this.unitWidth + this.gutter) * 2
34926             });
34927             
34928             return pos;
34929             
34930         }
34931         
34932         if(box[0].size == 'xs' && box[1].size == 'xs'){
34933             
34934             pos.push({
34935                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34936                 y : minY
34937             });
34938
34939             pos.push({
34940                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34941                 y : minY
34942             });
34943             
34944             pos.push({
34945                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34946                 y : minY + (this.unitWidth + this.gutter) * 1
34947             });
34948             
34949             return pos;
34950             
34951         }
34952         
34953         pos.push({
34954             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34955             y : minY
34956         });
34957
34958         pos.push({
34959             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34960             y : minY + (this.unitWidth + this.gutter) * 2
34961         });
34962
34963         pos.push({
34964             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34965             y : minY + (this.unitWidth + this.gutter) * 2
34966         });
34967             
34968         return pos;
34969         
34970     },
34971     
34972     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34973     {
34974         var pos = [];
34975         
34976         if(box[0].size == 'xs'){
34977             
34978             pos.push({
34979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34980                 y : minY
34981             });
34982
34983             pos.push({
34984                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34985                 y : minY
34986             });
34987             
34988             pos.push({
34989                 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),
34990                 y : minY
34991             });
34992             
34993             pos.push({
34994                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34995                 y : minY + (this.unitWidth + this.gutter) * 1
34996             });
34997             
34998             return pos;
34999             
35000         }
35001         
35002         pos.push({
35003             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35004             y : minY
35005         });
35006         
35007         pos.push({
35008             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35009             y : minY + (this.unitWidth + this.gutter) * 2
35010         });
35011         
35012         pos.push({
35013             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35014             y : minY + (this.unitWidth + this.gutter) * 2
35015         });
35016         
35017         pos.push({
35018             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),
35019             y : minY + (this.unitWidth + this.gutter) * 2
35020         });
35021
35022         return pos;
35023         
35024     },
35025     
35026     /**
35027     * remove a Masonry Brick
35028     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35029     */
35030     removeBrick : function(brick_id)
35031     {
35032         if (!brick_id) {
35033             return;
35034         }
35035         
35036         for (var i = 0; i<this.bricks.length; i++) {
35037             if (this.bricks[i].id == brick_id) {
35038                 this.bricks.splice(i,1);
35039                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35040                 this.initial();
35041             }
35042         }
35043     },
35044     
35045     /**
35046     * adds a Masonry Brick
35047     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35048     */
35049     addBrick : function(cfg)
35050     {
35051         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35052         //this.register(cn);
35053         cn.parentId = this.id;
35054         cn.render(this.el);
35055         return cn;
35056     },
35057     
35058     /**
35059     * register a Masonry Brick
35060     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35061     */
35062     
35063     register : function(brick)
35064     {
35065         this.bricks.push(brick);
35066         brick.masonryId = this.id;
35067     },
35068     
35069     /**
35070     * clear all the Masonry Brick
35071     */
35072     clearAll : function()
35073     {
35074         this.bricks = [];
35075         //this.getChildContainer().dom.innerHTML = "";
35076         this.el.dom.innerHTML = '';
35077     },
35078     
35079     getSelected : function()
35080     {
35081         if (!this.selectedBrick) {
35082             return false;
35083         }
35084         
35085         return this.selectedBrick;
35086     }
35087 });
35088
35089 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35090     
35091     groups: {},
35092      /**
35093     * register a Masonry Layout
35094     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35095     */
35096     
35097     register : function(layout)
35098     {
35099         this.groups[layout.id] = layout;
35100     },
35101     /**
35102     * fetch a  Masonry Layout based on the masonry layout ID
35103     * @param {string} the masonry layout to add
35104     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35105     */
35106     
35107     get: function(layout_id) {
35108         if (typeof(this.groups[layout_id]) == 'undefined') {
35109             return false;
35110         }
35111         return this.groups[layout_id] ;
35112     }
35113     
35114     
35115     
35116 });
35117
35118  
35119
35120  /**
35121  *
35122  * This is based on 
35123  * http://masonry.desandro.com
35124  *
35125  * The idea is to render all the bricks based on vertical width...
35126  *
35127  * The original code extends 'outlayer' - we might need to use that....
35128  * 
35129  */
35130
35131
35132 /**
35133  * @class Roo.bootstrap.LayoutMasonryAuto
35134  * @extends Roo.bootstrap.Component
35135  * Bootstrap Layout Masonry class
35136  * 
35137  * @constructor
35138  * Create a new Element
35139  * @param {Object} config The config object
35140  */
35141
35142 Roo.bootstrap.LayoutMasonryAuto = function(config){
35143     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35144 };
35145
35146 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35147     
35148       /**
35149      * @cfg {Boolean} isFitWidth  - resize the width..
35150      */   
35151     isFitWidth : false,  // options..
35152     /**
35153      * @cfg {Boolean} isOriginLeft = left align?
35154      */   
35155     isOriginLeft : true,
35156     /**
35157      * @cfg {Boolean} isOriginTop = top align?
35158      */   
35159     isOriginTop : false,
35160     /**
35161      * @cfg {Boolean} isLayoutInstant = no animation?
35162      */   
35163     isLayoutInstant : false, // needed?
35164     /**
35165      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35166      */   
35167     isResizingContainer : true,
35168     /**
35169      * @cfg {Number} columnWidth  width of the columns 
35170      */   
35171     
35172     columnWidth : 0,
35173     
35174     /**
35175      * @cfg {Number} maxCols maximum number of columns
35176      */   
35177     
35178     maxCols: 0,
35179     /**
35180      * @cfg {Number} padHeight padding below box..
35181      */   
35182     
35183     padHeight : 10, 
35184     
35185     /**
35186      * @cfg {Boolean} isAutoInitial defalut true
35187      */   
35188     
35189     isAutoInitial : true, 
35190     
35191     // private?
35192     gutter : 0,
35193     
35194     containerWidth: 0,
35195     initialColumnWidth : 0,
35196     currentSize : null,
35197     
35198     colYs : null, // array.
35199     maxY : 0,
35200     padWidth: 10,
35201     
35202     
35203     tag: 'div',
35204     cls: '',
35205     bricks: null, //CompositeElement
35206     cols : 0, // array?
35207     // element : null, // wrapped now this.el
35208     _isLayoutInited : null, 
35209     
35210     
35211     getAutoCreate : function(){
35212         
35213         var cfg = {
35214             tag: this.tag,
35215             cls: 'blog-masonary-wrapper ' + this.cls,
35216             cn : {
35217                 cls : 'mas-boxes masonary'
35218             }
35219         };
35220         
35221         return cfg;
35222     },
35223     
35224     getChildContainer: function( )
35225     {
35226         if (this.boxesEl) {
35227             return this.boxesEl;
35228         }
35229         
35230         this.boxesEl = this.el.select('.mas-boxes').first();
35231         
35232         return this.boxesEl;
35233     },
35234     
35235     
35236     initEvents : function()
35237     {
35238         var _this = this;
35239         
35240         if(this.isAutoInitial){
35241             Roo.log('hook children rendered');
35242             this.on('childrenrendered', function() {
35243                 Roo.log('children rendered');
35244                 _this.initial();
35245             } ,this);
35246         }
35247         
35248     },
35249     
35250     initial : function()
35251     {
35252         this.reloadItems();
35253
35254         this.currentSize = this.el.getBox(true);
35255
35256         /// was window resize... - let's see if this works..
35257         Roo.EventManager.onWindowResize(this.resize, this); 
35258
35259         if(!this.isAutoInitial){
35260             this.layout();
35261             return;
35262         }
35263         
35264         this.layout.defer(500,this);
35265     },
35266     
35267     reloadItems: function()
35268     {
35269         this.bricks = this.el.select('.masonry-brick', true);
35270         
35271         this.bricks.each(function(b) {
35272             //Roo.log(b.getSize());
35273             if (!b.attr('originalwidth')) {
35274                 b.attr('originalwidth',  b.getSize().width);
35275             }
35276             
35277         });
35278         
35279         Roo.log(this.bricks.elements.length);
35280     },
35281     
35282     resize : function()
35283     {
35284         Roo.log('resize');
35285         var cs = this.el.getBox(true);
35286         
35287         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35288             Roo.log("no change in with or X");
35289             return;
35290         }
35291         this.currentSize = cs;
35292         this.layout();
35293     },
35294     
35295     layout : function()
35296     {
35297          Roo.log('layout');
35298         this._resetLayout();
35299         //this._manageStamps();
35300       
35301         // don't animate first layout
35302         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35303         this.layoutItems( isInstant );
35304       
35305         // flag for initalized
35306         this._isLayoutInited = true;
35307     },
35308     
35309     layoutItems : function( isInstant )
35310     {
35311         //var items = this._getItemsForLayout( this.items );
35312         // original code supports filtering layout items.. we just ignore it..
35313         
35314         this._layoutItems( this.bricks , isInstant );
35315       
35316         this._postLayout();
35317     },
35318     _layoutItems : function ( items , isInstant)
35319     {
35320        //this.fireEvent( 'layout', this, items );
35321     
35322
35323         if ( !items || !items.elements.length ) {
35324           // no items, emit event with empty array
35325             return;
35326         }
35327
35328         var queue = [];
35329         items.each(function(item) {
35330             Roo.log("layout item");
35331             Roo.log(item);
35332             // get x/y object from method
35333             var position = this._getItemLayoutPosition( item );
35334             // enqueue
35335             position.item = item;
35336             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35337             queue.push( position );
35338         }, this);
35339       
35340         this._processLayoutQueue( queue );
35341     },
35342     /** Sets position of item in DOM
35343     * @param {Element} item
35344     * @param {Number} x - horizontal position
35345     * @param {Number} y - vertical position
35346     * @param {Boolean} isInstant - disables transitions
35347     */
35348     _processLayoutQueue : function( queue )
35349     {
35350         for ( var i=0, len = queue.length; i < len; i++ ) {
35351             var obj = queue[i];
35352             obj.item.position('absolute');
35353             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35354         }
35355     },
35356       
35357     
35358     /**
35359     * Any logic you want to do after each layout,
35360     * i.e. size the container
35361     */
35362     _postLayout : function()
35363     {
35364         this.resizeContainer();
35365     },
35366     
35367     resizeContainer : function()
35368     {
35369         if ( !this.isResizingContainer ) {
35370             return;
35371         }
35372         var size = this._getContainerSize();
35373         if ( size ) {
35374             this.el.setSize(size.width,size.height);
35375             this.boxesEl.setSize(size.width,size.height);
35376         }
35377     },
35378     
35379     
35380     
35381     _resetLayout : function()
35382     {
35383         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35384         this.colWidth = this.el.getWidth();
35385         //this.gutter = this.el.getWidth(); 
35386         
35387         this.measureColumns();
35388
35389         // reset column Y
35390         var i = this.cols;
35391         this.colYs = [];
35392         while (i--) {
35393             this.colYs.push( 0 );
35394         }
35395     
35396         this.maxY = 0;
35397     },
35398
35399     measureColumns : function()
35400     {
35401         this.getContainerWidth();
35402       // if columnWidth is 0, default to outerWidth of first item
35403         if ( !this.columnWidth ) {
35404             var firstItem = this.bricks.first();
35405             Roo.log(firstItem);
35406             this.columnWidth  = this.containerWidth;
35407             if (firstItem && firstItem.attr('originalwidth') ) {
35408                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35409             }
35410             // columnWidth fall back to item of first element
35411             Roo.log("set column width?");
35412                         this.initialColumnWidth = this.columnWidth  ;
35413
35414             // if first elem has no width, default to size of container
35415             
35416         }
35417         
35418         
35419         if (this.initialColumnWidth) {
35420             this.columnWidth = this.initialColumnWidth;
35421         }
35422         
35423         
35424             
35425         // column width is fixed at the top - however if container width get's smaller we should
35426         // reduce it...
35427         
35428         // this bit calcs how man columns..
35429             
35430         var columnWidth = this.columnWidth += this.gutter;
35431       
35432         // calculate columns
35433         var containerWidth = this.containerWidth + this.gutter;
35434         
35435         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35436         // fix rounding errors, typically with gutters
35437         var excess = columnWidth - containerWidth % columnWidth;
35438         
35439         
35440         // if overshoot is less than a pixel, round up, otherwise floor it
35441         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35442         cols = Math[ mathMethod ]( cols );
35443         this.cols = Math.max( cols, 1 );
35444         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35445         
35446          // padding positioning..
35447         var totalColWidth = this.cols * this.columnWidth;
35448         var padavail = this.containerWidth - totalColWidth;
35449         // so for 2 columns - we need 3 'pads'
35450         
35451         var padNeeded = (1+this.cols) * this.padWidth;
35452         
35453         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35454         
35455         this.columnWidth += padExtra
35456         //this.padWidth = Math.floor(padavail /  ( this.cols));
35457         
35458         // adjust colum width so that padding is fixed??
35459         
35460         // we have 3 columns ... total = width * 3
35461         // we have X left over... that should be used by 
35462         
35463         //if (this.expandC) {
35464             
35465         //}
35466         
35467         
35468         
35469     },
35470     
35471     getContainerWidth : function()
35472     {
35473        /* // container is parent if fit width
35474         var container = this.isFitWidth ? this.element.parentNode : this.element;
35475         // check that this.size and size are there
35476         // IE8 triggers resize on body size change, so they might not be
35477         
35478         var size = getSize( container );  //FIXME
35479         this.containerWidth = size && size.innerWidth; //FIXME
35480         */
35481          
35482         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35483         
35484     },
35485     
35486     _getItemLayoutPosition : function( item )  // what is item?
35487     {
35488         // we resize the item to our columnWidth..
35489       
35490         item.setWidth(this.columnWidth);
35491         item.autoBoxAdjust  = false;
35492         
35493         var sz = item.getSize();
35494  
35495         // how many columns does this brick span
35496         var remainder = this.containerWidth % this.columnWidth;
35497         
35498         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35499         // round if off by 1 pixel, otherwise use ceil
35500         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35501         colSpan = Math.min( colSpan, this.cols );
35502         
35503         // normally this should be '1' as we dont' currently allow multi width columns..
35504         
35505         var colGroup = this._getColGroup( colSpan );
35506         // get the minimum Y value from the columns
35507         var minimumY = Math.min.apply( Math, colGroup );
35508         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35509         
35510         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35511          
35512         // position the brick
35513         var position = {
35514             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35515             y: this.currentSize.y + minimumY + this.padHeight
35516         };
35517         
35518         Roo.log(position);
35519         // apply setHeight to necessary columns
35520         var setHeight = minimumY + sz.height + this.padHeight;
35521         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35522         
35523         var setSpan = this.cols + 1 - colGroup.length;
35524         for ( var i = 0; i < setSpan; i++ ) {
35525           this.colYs[ shortColIndex + i ] = setHeight ;
35526         }
35527       
35528         return position;
35529     },
35530     
35531     /**
35532      * @param {Number} colSpan - number of columns the element spans
35533      * @returns {Array} colGroup
35534      */
35535     _getColGroup : function( colSpan )
35536     {
35537         if ( colSpan < 2 ) {
35538           // if brick spans only one column, use all the column Ys
35539           return this.colYs;
35540         }
35541       
35542         var colGroup = [];
35543         // how many different places could this brick fit horizontally
35544         var groupCount = this.cols + 1 - colSpan;
35545         // for each group potential horizontal position
35546         for ( var i = 0; i < groupCount; i++ ) {
35547           // make an array of colY values for that one group
35548           var groupColYs = this.colYs.slice( i, i + colSpan );
35549           // and get the max value of the array
35550           colGroup[i] = Math.max.apply( Math, groupColYs );
35551         }
35552         return colGroup;
35553     },
35554     /*
35555     _manageStamp : function( stamp )
35556     {
35557         var stampSize =  stamp.getSize();
35558         var offset = stamp.getBox();
35559         // get the columns that this stamp affects
35560         var firstX = this.isOriginLeft ? offset.x : offset.right;
35561         var lastX = firstX + stampSize.width;
35562         var firstCol = Math.floor( firstX / this.columnWidth );
35563         firstCol = Math.max( 0, firstCol );
35564         
35565         var lastCol = Math.floor( lastX / this.columnWidth );
35566         // lastCol should not go over if multiple of columnWidth #425
35567         lastCol -= lastX % this.columnWidth ? 0 : 1;
35568         lastCol = Math.min( this.cols - 1, lastCol );
35569         
35570         // set colYs to bottom of the stamp
35571         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35572             stampSize.height;
35573             
35574         for ( var i = firstCol; i <= lastCol; i++ ) {
35575           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35576         }
35577     },
35578     */
35579     
35580     _getContainerSize : function()
35581     {
35582         this.maxY = Math.max.apply( Math, this.colYs );
35583         var size = {
35584             height: this.maxY
35585         };
35586       
35587         if ( this.isFitWidth ) {
35588             size.width = this._getContainerFitWidth();
35589         }
35590       
35591         return size;
35592     },
35593     
35594     _getContainerFitWidth : function()
35595     {
35596         var unusedCols = 0;
35597         // count unused columns
35598         var i = this.cols;
35599         while ( --i ) {
35600           if ( this.colYs[i] !== 0 ) {
35601             break;
35602           }
35603           unusedCols++;
35604         }
35605         // fit container to columns that have been used
35606         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35607     },
35608     
35609     needsResizeLayout : function()
35610     {
35611         var previousWidth = this.containerWidth;
35612         this.getContainerWidth();
35613         return previousWidth !== this.containerWidth;
35614     }
35615  
35616 });
35617
35618  
35619
35620  /*
35621  * - LGPL
35622  *
35623  * element
35624  * 
35625  */
35626
35627 /**
35628  * @class Roo.bootstrap.MasonryBrick
35629  * @extends Roo.bootstrap.Component
35630  * Bootstrap MasonryBrick class
35631  * 
35632  * @constructor
35633  * Create a new MasonryBrick
35634  * @param {Object} config The config object
35635  */
35636
35637 Roo.bootstrap.MasonryBrick = function(config){
35638     
35639     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35640     
35641     Roo.bootstrap.MasonryBrick.register(this);
35642     
35643     this.addEvents({
35644         // raw events
35645         /**
35646          * @event click
35647          * When a MasonryBrick is clcik
35648          * @param {Roo.bootstrap.MasonryBrick} this
35649          * @param {Roo.EventObject} e
35650          */
35651         "click" : true
35652     });
35653 };
35654
35655 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35656     
35657     /**
35658      * @cfg {String} title
35659      */   
35660     title : '',
35661     /**
35662      * @cfg {String} html
35663      */   
35664     html : '',
35665     /**
35666      * @cfg {String} bgimage
35667      */   
35668     bgimage : '',
35669     /**
35670      * @cfg {String} videourl
35671      */   
35672     videourl : '',
35673     /**
35674      * @cfg {String} cls
35675      */   
35676     cls : '',
35677     /**
35678      * @cfg {String} href
35679      */   
35680     href : '',
35681     /**
35682      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35683      */   
35684     size : 'xs',
35685     
35686     /**
35687      * @cfg {String} placetitle (center|bottom)
35688      */   
35689     placetitle : '',
35690     
35691     /**
35692      * @cfg {Boolean} isFitContainer defalut true
35693      */   
35694     isFitContainer : true, 
35695     
35696     /**
35697      * @cfg {Boolean} preventDefault defalut false
35698      */   
35699     preventDefault : false, 
35700     
35701     /**
35702      * @cfg {Boolean} inverse defalut false
35703      */   
35704     maskInverse : false, 
35705     
35706     getAutoCreate : function()
35707     {
35708         if(!this.isFitContainer){
35709             return this.getSplitAutoCreate();
35710         }
35711         
35712         var cls = 'masonry-brick masonry-brick-full';
35713         
35714         if(this.href.length){
35715             cls += ' masonry-brick-link';
35716         }
35717         
35718         if(this.bgimage.length){
35719             cls += ' masonry-brick-image';
35720         }
35721         
35722         if(this.maskInverse){
35723             cls += ' mask-inverse';
35724         }
35725         
35726         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35727             cls += ' enable-mask';
35728         }
35729         
35730         if(this.size){
35731             cls += ' masonry-' + this.size + '-brick';
35732         }
35733         
35734         if(this.placetitle.length){
35735             
35736             switch (this.placetitle) {
35737                 case 'center' :
35738                     cls += ' masonry-center-title';
35739                     break;
35740                 case 'bottom' :
35741                     cls += ' masonry-bottom-title';
35742                     break;
35743                 default:
35744                     break;
35745             }
35746             
35747         } else {
35748             if(!this.html.length && !this.bgimage.length){
35749                 cls += ' masonry-center-title';
35750             }
35751
35752             if(!this.html.length && this.bgimage.length){
35753                 cls += ' masonry-bottom-title';
35754             }
35755         }
35756         
35757         if(this.cls){
35758             cls += ' ' + this.cls;
35759         }
35760         
35761         var cfg = {
35762             tag: (this.href.length) ? 'a' : 'div',
35763             cls: cls,
35764             cn: [
35765                 {
35766                     tag: 'div',
35767                     cls: 'masonry-brick-mask'
35768                 },
35769                 {
35770                     tag: 'div',
35771                     cls: 'masonry-brick-paragraph',
35772                     cn: []
35773                 }
35774             ]
35775         };
35776         
35777         if(this.href.length){
35778             cfg.href = this.href;
35779         }
35780         
35781         var cn = cfg.cn[1].cn;
35782         
35783         if(this.title.length){
35784             cn.push({
35785                 tag: 'h4',
35786                 cls: 'masonry-brick-title',
35787                 html: this.title
35788             });
35789         }
35790         
35791         if(this.html.length){
35792             cn.push({
35793                 tag: 'p',
35794                 cls: 'masonry-brick-text',
35795                 html: this.html
35796             });
35797         }
35798         
35799         if (!this.title.length && !this.html.length) {
35800             cfg.cn[1].cls += ' hide';
35801         }
35802         
35803         if(this.bgimage.length){
35804             cfg.cn.push({
35805                 tag: 'img',
35806                 cls: 'masonry-brick-image-view',
35807                 src: this.bgimage
35808             });
35809         }
35810         
35811         if(this.videourl.length){
35812             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35813             // youtube support only?
35814             cfg.cn.push({
35815                 tag: 'iframe',
35816                 cls: 'masonry-brick-image-view',
35817                 src: vurl,
35818                 frameborder : 0,
35819                 allowfullscreen : true
35820             });
35821         }
35822         
35823         return cfg;
35824         
35825     },
35826     
35827     getSplitAutoCreate : function()
35828     {
35829         var cls = 'masonry-brick masonry-brick-split';
35830         
35831         if(this.href.length){
35832             cls += ' masonry-brick-link';
35833         }
35834         
35835         if(this.bgimage.length){
35836             cls += ' masonry-brick-image';
35837         }
35838         
35839         if(this.size){
35840             cls += ' masonry-' + this.size + '-brick';
35841         }
35842         
35843         switch (this.placetitle) {
35844             case 'center' :
35845                 cls += ' masonry-center-title';
35846                 break;
35847             case 'bottom' :
35848                 cls += ' masonry-bottom-title';
35849                 break;
35850             default:
35851                 if(!this.bgimage.length){
35852                     cls += ' masonry-center-title';
35853                 }
35854
35855                 if(this.bgimage.length){
35856                     cls += ' masonry-bottom-title';
35857                 }
35858                 break;
35859         }
35860         
35861         if(this.cls){
35862             cls += ' ' + this.cls;
35863         }
35864         
35865         var cfg = {
35866             tag: (this.href.length) ? 'a' : 'div',
35867             cls: cls,
35868             cn: [
35869                 {
35870                     tag: 'div',
35871                     cls: 'masonry-brick-split-head',
35872                     cn: [
35873                         {
35874                             tag: 'div',
35875                             cls: 'masonry-brick-paragraph',
35876                             cn: []
35877                         }
35878                     ]
35879                 },
35880                 {
35881                     tag: 'div',
35882                     cls: 'masonry-brick-split-body',
35883                     cn: []
35884                 }
35885             ]
35886         };
35887         
35888         if(this.href.length){
35889             cfg.href = this.href;
35890         }
35891         
35892         if(this.title.length){
35893             cfg.cn[0].cn[0].cn.push({
35894                 tag: 'h4',
35895                 cls: 'masonry-brick-title',
35896                 html: this.title
35897             });
35898         }
35899         
35900         if(this.html.length){
35901             cfg.cn[1].cn.push({
35902                 tag: 'p',
35903                 cls: 'masonry-brick-text',
35904                 html: this.html
35905             });
35906         }
35907
35908         if(this.bgimage.length){
35909             cfg.cn[0].cn.push({
35910                 tag: 'img',
35911                 cls: 'masonry-brick-image-view',
35912                 src: this.bgimage
35913             });
35914         }
35915         
35916         if(this.videourl.length){
35917             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35918             // youtube support only?
35919             cfg.cn[0].cn.cn.push({
35920                 tag: 'iframe',
35921                 cls: 'masonry-brick-image-view',
35922                 src: vurl,
35923                 frameborder : 0,
35924                 allowfullscreen : true
35925             });
35926         }
35927         
35928         return cfg;
35929     },
35930     
35931     initEvents: function() 
35932     {
35933         switch (this.size) {
35934             case 'xs' :
35935                 this.x = 1;
35936                 this.y = 1;
35937                 break;
35938             case 'sm' :
35939                 this.x = 2;
35940                 this.y = 2;
35941                 break;
35942             case 'md' :
35943             case 'md-left' :
35944             case 'md-right' :
35945                 this.x = 3;
35946                 this.y = 3;
35947                 break;
35948             case 'tall' :
35949                 this.x = 2;
35950                 this.y = 3;
35951                 break;
35952             case 'wide' :
35953                 this.x = 3;
35954                 this.y = 2;
35955                 break;
35956             case 'wide-thin' :
35957                 this.x = 3;
35958                 this.y = 1;
35959                 break;
35960                         
35961             default :
35962                 break;
35963         }
35964         
35965         if(Roo.isTouch){
35966             this.el.on('touchstart', this.onTouchStart, this);
35967             this.el.on('touchmove', this.onTouchMove, this);
35968             this.el.on('touchend', this.onTouchEnd, this);
35969             this.el.on('contextmenu', this.onContextMenu, this);
35970         } else {
35971             this.el.on('mouseenter'  ,this.enter, this);
35972             this.el.on('mouseleave', this.leave, this);
35973             this.el.on('click', this.onClick, this);
35974         }
35975         
35976         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35977             this.parent().bricks.push(this);   
35978         }
35979         
35980     },
35981     
35982     onClick: function(e, el)
35983     {
35984         var time = this.endTimer - this.startTimer;
35985         // Roo.log(e.preventDefault());
35986         if(Roo.isTouch){
35987             if(time > 1000){
35988                 e.preventDefault();
35989                 return;
35990             }
35991         }
35992         
35993         if(!this.preventDefault){
35994             return;
35995         }
35996         
35997         e.preventDefault();
35998         
35999         if (this.activeClass != '') {
36000             this.selectBrick();
36001         }
36002         
36003         this.fireEvent('click', this, e);
36004     },
36005     
36006     enter: function(e, el)
36007     {
36008         e.preventDefault();
36009         
36010         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36011             return;
36012         }
36013         
36014         if(this.bgimage.length && this.html.length){
36015             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36016         }
36017     },
36018     
36019     leave: function(e, el)
36020     {
36021         e.preventDefault();
36022         
36023         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36024             return;
36025         }
36026         
36027         if(this.bgimage.length && this.html.length){
36028             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36029         }
36030     },
36031     
36032     onTouchStart: function(e, el)
36033     {
36034 //        e.preventDefault();
36035         
36036         this.touchmoved = false;
36037         
36038         if(!this.isFitContainer){
36039             return;
36040         }
36041         
36042         if(!this.bgimage.length || !this.html.length){
36043             return;
36044         }
36045         
36046         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36047         
36048         this.timer = new Date().getTime();
36049         
36050     },
36051     
36052     onTouchMove: function(e, el)
36053     {
36054         this.touchmoved = true;
36055     },
36056     
36057     onContextMenu : function(e,el)
36058     {
36059         e.preventDefault();
36060         e.stopPropagation();
36061         return false;
36062     },
36063     
36064     onTouchEnd: function(e, el)
36065     {
36066 //        e.preventDefault();
36067         
36068         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36069         
36070             this.leave(e,el);
36071             
36072             return;
36073         }
36074         
36075         if(!this.bgimage.length || !this.html.length){
36076             
36077             if(this.href.length){
36078                 window.location.href = this.href;
36079             }
36080             
36081             return;
36082         }
36083         
36084         if(!this.isFitContainer){
36085             return;
36086         }
36087         
36088         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36089         
36090         window.location.href = this.href;
36091     },
36092     
36093     //selection on single brick only
36094     selectBrick : function() {
36095         
36096         if (!this.parentId) {
36097             return;
36098         }
36099         
36100         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36101         var index = m.selectedBrick.indexOf(this.id);
36102         
36103         if ( index > -1) {
36104             m.selectedBrick.splice(index,1);
36105             this.el.removeClass(this.activeClass);
36106             return;
36107         }
36108         
36109         for(var i = 0; i < m.selectedBrick.length; i++) {
36110             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36111             b.el.removeClass(b.activeClass);
36112         }
36113         
36114         m.selectedBrick = [];
36115         
36116         m.selectedBrick.push(this.id);
36117         this.el.addClass(this.activeClass);
36118         return;
36119     },
36120     
36121     isSelected : function(){
36122         return this.el.hasClass(this.activeClass);
36123         
36124     }
36125 });
36126
36127 Roo.apply(Roo.bootstrap.MasonryBrick, {
36128     
36129     //groups: {},
36130     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36131      /**
36132     * register a Masonry Brick
36133     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36134     */
36135     
36136     register : function(brick)
36137     {
36138         //this.groups[brick.id] = brick;
36139         this.groups.add(brick.id, brick);
36140     },
36141     /**
36142     * fetch a  masonry brick based on the masonry brick ID
36143     * @param {string} the masonry brick to add
36144     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36145     */
36146     
36147     get: function(brick_id) 
36148     {
36149         // if (typeof(this.groups[brick_id]) == 'undefined') {
36150         //     return false;
36151         // }
36152         // return this.groups[brick_id] ;
36153         
36154         if(this.groups.key(brick_id)) {
36155             return this.groups.key(brick_id);
36156         }
36157         
36158         return false;
36159     }
36160     
36161     
36162     
36163 });
36164
36165  /*
36166  * - LGPL
36167  *
36168  * element
36169  * 
36170  */
36171
36172 /**
36173  * @class Roo.bootstrap.Brick
36174  * @extends Roo.bootstrap.Component
36175  * Bootstrap Brick class
36176  * 
36177  * @constructor
36178  * Create a new Brick
36179  * @param {Object} config The config object
36180  */
36181
36182 Roo.bootstrap.Brick = function(config){
36183     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36184     
36185     this.addEvents({
36186         // raw events
36187         /**
36188          * @event click
36189          * When a Brick is click
36190          * @param {Roo.bootstrap.Brick} this
36191          * @param {Roo.EventObject} e
36192          */
36193         "click" : true
36194     });
36195 };
36196
36197 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36198     
36199     /**
36200      * @cfg {String} title
36201      */   
36202     title : '',
36203     /**
36204      * @cfg {String} html
36205      */   
36206     html : '',
36207     /**
36208      * @cfg {String} bgimage
36209      */   
36210     bgimage : '',
36211     /**
36212      * @cfg {String} cls
36213      */   
36214     cls : '',
36215     /**
36216      * @cfg {String} href
36217      */   
36218     href : '',
36219     /**
36220      * @cfg {String} video
36221      */   
36222     video : '',
36223     /**
36224      * @cfg {Boolean} square
36225      */   
36226     square : true,
36227     
36228     getAutoCreate : function()
36229     {
36230         var cls = 'roo-brick';
36231         
36232         if(this.href.length){
36233             cls += ' roo-brick-link';
36234         }
36235         
36236         if(this.bgimage.length){
36237             cls += ' roo-brick-image';
36238         }
36239         
36240         if(!this.html.length && !this.bgimage.length){
36241             cls += ' roo-brick-center-title';
36242         }
36243         
36244         if(!this.html.length && this.bgimage.length){
36245             cls += ' roo-brick-bottom-title';
36246         }
36247         
36248         if(this.cls){
36249             cls += ' ' + this.cls;
36250         }
36251         
36252         var cfg = {
36253             tag: (this.href.length) ? 'a' : 'div',
36254             cls: cls,
36255             cn: [
36256                 {
36257                     tag: 'div',
36258                     cls: 'roo-brick-paragraph',
36259                     cn: []
36260                 }
36261             ]
36262         };
36263         
36264         if(this.href.length){
36265             cfg.href = this.href;
36266         }
36267         
36268         var cn = cfg.cn[0].cn;
36269         
36270         if(this.title.length){
36271             cn.push({
36272                 tag: 'h4',
36273                 cls: 'roo-brick-title',
36274                 html: this.title
36275             });
36276         }
36277         
36278         if(this.html.length){
36279             cn.push({
36280                 tag: 'p',
36281                 cls: 'roo-brick-text',
36282                 html: this.html
36283             });
36284         } else {
36285             cn.cls += ' hide';
36286         }
36287         
36288         if(this.bgimage.length){
36289             cfg.cn.push({
36290                 tag: 'img',
36291                 cls: 'roo-brick-image-view',
36292                 src: this.bgimage
36293             });
36294         }
36295         
36296         return cfg;
36297     },
36298     
36299     initEvents: function() 
36300     {
36301         if(this.title.length || this.html.length){
36302             this.el.on('mouseenter'  ,this.enter, this);
36303             this.el.on('mouseleave', this.leave, this);
36304         }
36305         
36306         Roo.EventManager.onWindowResize(this.resize, this); 
36307         
36308         if(this.bgimage.length){
36309             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36310             this.imageEl.on('load', this.onImageLoad, this);
36311             return;
36312         }
36313         
36314         this.resize();
36315     },
36316     
36317     onImageLoad : function()
36318     {
36319         this.resize();
36320     },
36321     
36322     resize : function()
36323     {
36324         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36325         
36326         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36327         
36328         if(this.bgimage.length){
36329             var image = this.el.select('.roo-brick-image-view', true).first();
36330             
36331             image.setWidth(paragraph.getWidth());
36332             
36333             if(this.square){
36334                 image.setHeight(paragraph.getWidth());
36335             }
36336             
36337             this.el.setHeight(image.getHeight());
36338             paragraph.setHeight(image.getHeight());
36339             
36340         }
36341         
36342     },
36343     
36344     enter: function(e, el)
36345     {
36346         e.preventDefault();
36347         
36348         if(this.bgimage.length){
36349             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36350             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36351         }
36352     },
36353     
36354     leave: function(e, el)
36355     {
36356         e.preventDefault();
36357         
36358         if(this.bgimage.length){
36359             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36360             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36361         }
36362     }
36363     
36364 });
36365
36366  
36367
36368  /*
36369  * - LGPL
36370  *
36371  * Number field 
36372  */
36373
36374 /**
36375  * @class Roo.bootstrap.NumberField
36376  * @extends Roo.bootstrap.Input
36377  * Bootstrap NumberField class
36378  * 
36379  * 
36380  * 
36381  * 
36382  * @constructor
36383  * Create a new NumberField
36384  * @param {Object} config The config object
36385  */
36386
36387 Roo.bootstrap.NumberField = function(config){
36388     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36389 };
36390
36391 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36392     
36393     /**
36394      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36395      */
36396     allowDecimals : true,
36397     /**
36398      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36399      */
36400     decimalSeparator : ".",
36401     /**
36402      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36403      */
36404     decimalPrecision : 2,
36405     /**
36406      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36407      */
36408     allowNegative : true,
36409     
36410     /**
36411      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36412      */
36413     allowZero: true,
36414     /**
36415      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36416      */
36417     minValue : Number.NEGATIVE_INFINITY,
36418     /**
36419      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36420      */
36421     maxValue : Number.MAX_VALUE,
36422     /**
36423      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36424      */
36425     minText : "The minimum value for this field is {0}",
36426     /**
36427      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36428      */
36429     maxText : "The maximum value for this field is {0}",
36430     /**
36431      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36432      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36433      */
36434     nanText : "{0} is not a valid number",
36435     /**
36436      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36437      */
36438     thousandsDelimiter : false,
36439     /**
36440      * @cfg {String} valueAlign alignment of value
36441      */
36442     valueAlign : "left",
36443
36444     getAutoCreate : function()
36445     {
36446         var hiddenInput = {
36447             tag: 'input',
36448             type: 'hidden',
36449             id: Roo.id(),
36450             cls: 'hidden-number-input'
36451         };
36452         
36453         if (this.name) {
36454             hiddenInput.name = this.name;
36455         }
36456         
36457         this.name = '';
36458         
36459         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36460         
36461         this.name = hiddenInput.name;
36462         
36463         if(cfg.cn.length > 0) {
36464             cfg.cn.push(hiddenInput);
36465         }
36466         
36467         return cfg;
36468     },
36469
36470     // private
36471     initEvents : function()
36472     {   
36473         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36474         
36475         var allowed = "0123456789";
36476         
36477         if(this.allowDecimals){
36478             allowed += this.decimalSeparator;
36479         }
36480         
36481         if(this.allowNegative){
36482             allowed += "-";
36483         }
36484         
36485         if(this.thousandsDelimiter) {
36486             allowed += ",";
36487         }
36488         
36489         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36490         
36491         var keyPress = function(e){
36492             
36493             var k = e.getKey();
36494             
36495             var c = e.getCharCode();
36496             
36497             if(
36498                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36499                     allowed.indexOf(String.fromCharCode(c)) === -1
36500             ){
36501                 e.stopEvent();
36502                 return;
36503             }
36504             
36505             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36506                 return;
36507             }
36508             
36509             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36510                 e.stopEvent();
36511             }
36512         };
36513         
36514         this.el.on("keypress", keyPress, this);
36515     },
36516     
36517     validateValue : function(value)
36518     {
36519         
36520         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36521             return false;
36522         }
36523         
36524         var num = this.parseValue(value);
36525         
36526         if(isNaN(num)){
36527             this.markInvalid(String.format(this.nanText, value));
36528             return false;
36529         }
36530         
36531         if(num < this.minValue){
36532             this.markInvalid(String.format(this.minText, this.minValue));
36533             return false;
36534         }
36535         
36536         if(num > this.maxValue){
36537             this.markInvalid(String.format(this.maxText, this.maxValue));
36538             return false;
36539         }
36540         
36541         return true;
36542     },
36543
36544     getValue : function()
36545     {
36546         var v = this.hiddenEl().getValue();
36547         
36548         return this.fixPrecision(this.parseValue(v));
36549     },
36550
36551     parseValue : function(value)
36552     {
36553         if(this.thousandsDelimiter) {
36554             value += "";
36555             r = new RegExp(",", "g");
36556             value = value.replace(r, "");
36557         }
36558         
36559         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36560         return isNaN(value) ? '' : value;
36561     },
36562
36563     fixPrecision : function(value)
36564     {
36565         if(this.thousandsDelimiter) {
36566             value += "";
36567             r = new RegExp(",", "g");
36568             value = value.replace(r, "");
36569         }
36570         
36571         var nan = isNaN(value);
36572         
36573         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36574             return nan ? '' : value;
36575         }
36576         return parseFloat(value).toFixed(this.decimalPrecision);
36577     },
36578
36579     setValue : function(v)
36580     {
36581         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36582         
36583         this.value = v;
36584         
36585         if(this.rendered){
36586             
36587             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36588             
36589             this.inputEl().dom.value = (v == '') ? '' :
36590                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36591             
36592             if(!this.allowZero && v === '0') {
36593                 this.hiddenEl().dom.value = '';
36594                 this.inputEl().dom.value = '';
36595             }
36596             
36597             this.validate();
36598         }
36599     },
36600
36601     decimalPrecisionFcn : function(v)
36602     {
36603         return Math.floor(v);
36604     },
36605
36606     beforeBlur : function()
36607     {
36608         var v = this.parseValue(this.getRawValue());
36609         
36610         if(v || v === 0 || v === ''){
36611             this.setValue(v);
36612         }
36613     },
36614     
36615     hiddenEl : function()
36616     {
36617         return this.el.select('input.hidden-number-input',true).first();
36618     }
36619     
36620 });
36621
36622  
36623
36624 /*
36625 * Licence: LGPL
36626 */
36627
36628 /**
36629  * @class Roo.bootstrap.DocumentSlider
36630  * @extends Roo.bootstrap.Component
36631  * Bootstrap DocumentSlider class
36632  * 
36633  * @constructor
36634  * Create a new DocumentViewer
36635  * @param {Object} config The config object
36636  */
36637
36638 Roo.bootstrap.DocumentSlider = function(config){
36639     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36640     
36641     this.files = [];
36642     
36643     this.addEvents({
36644         /**
36645          * @event initial
36646          * Fire after initEvent
36647          * @param {Roo.bootstrap.DocumentSlider} this
36648          */
36649         "initial" : true,
36650         /**
36651          * @event update
36652          * Fire after update
36653          * @param {Roo.bootstrap.DocumentSlider} this
36654          */
36655         "update" : true,
36656         /**
36657          * @event click
36658          * Fire after click
36659          * @param {Roo.bootstrap.DocumentSlider} this
36660          */
36661         "click" : true
36662     });
36663 };
36664
36665 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36666     
36667     files : false,
36668     
36669     indicator : 0,
36670     
36671     getAutoCreate : function()
36672     {
36673         var cfg = {
36674             tag : 'div',
36675             cls : 'roo-document-slider',
36676             cn : [
36677                 {
36678                     tag : 'div',
36679                     cls : 'roo-document-slider-header',
36680                     cn : [
36681                         {
36682                             tag : 'div',
36683                             cls : 'roo-document-slider-header-title'
36684                         }
36685                     ]
36686                 },
36687                 {
36688                     tag : 'div',
36689                     cls : 'roo-document-slider-body',
36690                     cn : [
36691                         {
36692                             tag : 'div',
36693                             cls : 'roo-document-slider-prev',
36694                             cn : [
36695                                 {
36696                                     tag : 'i',
36697                                     cls : 'fa fa-chevron-left'
36698                                 }
36699                             ]
36700                         },
36701                         {
36702                             tag : 'div',
36703                             cls : 'roo-document-slider-thumb',
36704                             cn : [
36705                                 {
36706                                     tag : 'img',
36707                                     cls : 'roo-document-slider-image'
36708                                 }
36709                             ]
36710                         },
36711                         {
36712                             tag : 'div',
36713                             cls : 'roo-document-slider-next',
36714                             cn : [
36715                                 {
36716                                     tag : 'i',
36717                                     cls : 'fa fa-chevron-right'
36718                                 }
36719                             ]
36720                         }
36721                     ]
36722                 }
36723             ]
36724         };
36725         
36726         return cfg;
36727     },
36728     
36729     initEvents : function()
36730     {
36731         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36732         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36733         
36734         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36735         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36736         
36737         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36738         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36739         
36740         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36741         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36742         
36743         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36744         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36745         
36746         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36747         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36748         
36749         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36750         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36751         
36752         this.thumbEl.on('click', this.onClick, this);
36753         
36754         this.prevIndicator.on('click', this.prev, this);
36755         
36756         this.nextIndicator.on('click', this.next, this);
36757         
36758     },
36759     
36760     initial : function()
36761     {
36762         if(this.files.length){
36763             this.indicator = 1;
36764             this.update()
36765         }
36766         
36767         this.fireEvent('initial', this);
36768     },
36769     
36770     update : function()
36771     {
36772         this.imageEl.attr('src', this.files[this.indicator - 1]);
36773         
36774         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36775         
36776         this.prevIndicator.show();
36777         
36778         if(this.indicator == 1){
36779             this.prevIndicator.hide();
36780         }
36781         
36782         this.nextIndicator.show();
36783         
36784         if(this.indicator == this.files.length){
36785             this.nextIndicator.hide();
36786         }
36787         
36788         this.thumbEl.scrollTo('top');
36789         
36790         this.fireEvent('update', this);
36791     },
36792     
36793     onClick : function(e)
36794     {
36795         e.preventDefault();
36796         
36797         this.fireEvent('click', this);
36798     },
36799     
36800     prev : function(e)
36801     {
36802         e.preventDefault();
36803         
36804         this.indicator = Math.max(1, this.indicator - 1);
36805         
36806         this.update();
36807     },
36808     
36809     next : function(e)
36810     {
36811         e.preventDefault();
36812         
36813         this.indicator = Math.min(this.files.length, this.indicator + 1);
36814         
36815         this.update();
36816     }
36817 });
36818 /*
36819  * - LGPL
36820  *
36821  * RadioSet
36822  *
36823  *
36824  */
36825
36826 /**
36827  * @class Roo.bootstrap.RadioSet
36828  * @extends Roo.bootstrap.Input
36829  * Bootstrap RadioSet class
36830  * @cfg {String} indicatorpos (left|right) default left
36831  * @cfg {Boolean} inline (true|false) inline the element (default true)
36832  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36833  * @constructor
36834  * Create a new RadioSet
36835  * @param {Object} config The config object
36836  */
36837
36838 Roo.bootstrap.RadioSet = function(config){
36839     
36840     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36841     
36842     this.radioes = [];
36843     
36844     Roo.bootstrap.RadioSet.register(this);
36845     
36846     this.addEvents({
36847         /**
36848         * @event check
36849         * Fires when the element is checked or unchecked.
36850         * @param {Roo.bootstrap.RadioSet} this This radio
36851         * @param {Roo.bootstrap.Radio} item The checked item
36852         */
36853        check : true,
36854        /**
36855         * @event click
36856         * Fires when the element is click.
36857         * @param {Roo.bootstrap.RadioSet} this This radio set
36858         * @param {Roo.bootstrap.Radio} item The checked item
36859         * @param {Roo.EventObject} e The event object
36860         */
36861        click : true
36862     });
36863     
36864 };
36865
36866 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36867
36868     radioes : false,
36869     
36870     inline : true,
36871     
36872     weight : '',
36873     
36874     indicatorpos : 'left',
36875     
36876     getAutoCreate : function()
36877     {
36878         var label = {
36879             tag : 'label',
36880             cls : 'roo-radio-set-label',
36881             cn : [
36882                 {
36883                     tag : 'span',
36884                     html : this.fieldLabel
36885                 }
36886             ]
36887         };
36888         if (Roo.bootstrap.version == 3) {
36889             
36890             
36891             if(this.indicatorpos == 'left'){
36892                 label.cn.unshift({
36893                     tag : 'i',
36894                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36895                     tooltip : 'This field is required'
36896                 });
36897             } else {
36898                 label.cn.push({
36899                     tag : 'i',
36900                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36901                     tooltip : 'This field is required'
36902                 });
36903             }
36904         }
36905         var items = {
36906             tag : 'div',
36907             cls : 'roo-radio-set-items'
36908         };
36909         
36910         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36911         
36912         if (align === 'left' && this.fieldLabel.length) {
36913             
36914             items = {
36915                 cls : "roo-radio-set-right", 
36916                 cn: [
36917                     items
36918                 ]
36919             };
36920             
36921             if(this.labelWidth > 12){
36922                 label.style = "width: " + this.labelWidth + 'px';
36923             }
36924             
36925             if(this.labelWidth < 13 && this.labelmd == 0){
36926                 this.labelmd = this.labelWidth;
36927             }
36928             
36929             if(this.labellg > 0){
36930                 label.cls += ' col-lg-' + this.labellg;
36931                 items.cls += ' col-lg-' + (12 - this.labellg);
36932             }
36933             
36934             if(this.labelmd > 0){
36935                 label.cls += ' col-md-' + this.labelmd;
36936                 items.cls += ' col-md-' + (12 - this.labelmd);
36937             }
36938             
36939             if(this.labelsm > 0){
36940                 label.cls += ' col-sm-' + this.labelsm;
36941                 items.cls += ' col-sm-' + (12 - this.labelsm);
36942             }
36943             
36944             if(this.labelxs > 0){
36945                 label.cls += ' col-xs-' + this.labelxs;
36946                 items.cls += ' col-xs-' + (12 - this.labelxs);
36947             }
36948         }
36949         
36950         var cfg = {
36951             tag : 'div',
36952             cls : 'roo-radio-set',
36953             cn : [
36954                 {
36955                     tag : 'input',
36956                     cls : 'roo-radio-set-input',
36957                     type : 'hidden',
36958                     name : this.name,
36959                     value : this.value ? this.value :  ''
36960                 },
36961                 label,
36962                 items
36963             ]
36964         };
36965         
36966         if(this.weight.length){
36967             cfg.cls += ' roo-radio-' + this.weight;
36968         }
36969         
36970         if(this.inline) {
36971             cfg.cls += ' roo-radio-set-inline';
36972         }
36973         
36974         var settings=this;
36975         ['xs','sm','md','lg'].map(function(size){
36976             if (settings[size]) {
36977                 cfg.cls += ' col-' + size + '-' + settings[size];
36978             }
36979         });
36980         
36981         return cfg;
36982         
36983     },
36984
36985     initEvents : function()
36986     {
36987         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36988         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36989         
36990         if(!this.fieldLabel.length){
36991             this.labelEl.hide();
36992         }
36993         
36994         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36995         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36996         
36997         this.indicator = this.indicatorEl();
36998         
36999         if(this.indicator){
37000             this.indicator.addClass('invisible');
37001         }
37002         
37003         this.originalValue = this.getValue();
37004         
37005     },
37006     
37007     inputEl: function ()
37008     {
37009         return this.el.select('.roo-radio-set-input', true).first();
37010     },
37011     
37012     getChildContainer : function()
37013     {
37014         return this.itemsEl;
37015     },
37016     
37017     register : function(item)
37018     {
37019         this.radioes.push(item);
37020         
37021     },
37022     
37023     validate : function()
37024     {   
37025         if(this.getVisibilityEl().hasClass('hidden')){
37026             return true;
37027         }
37028         
37029         var valid = false;
37030         
37031         Roo.each(this.radioes, function(i){
37032             if(!i.checked){
37033                 return;
37034             }
37035             
37036             valid = true;
37037             return false;
37038         });
37039         
37040         if(this.allowBlank) {
37041             return true;
37042         }
37043         
37044         if(this.disabled || valid){
37045             this.markValid();
37046             return true;
37047         }
37048         
37049         this.markInvalid();
37050         return false;
37051         
37052     },
37053     
37054     markValid : function()
37055     {
37056         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37057             this.indicatorEl().removeClass('visible');
37058             this.indicatorEl().addClass('invisible');
37059         }
37060         
37061         
37062         if (Roo.bootstrap.version == 3) {
37063             this.el.removeClass([this.invalidClass, this.validClass]);
37064             this.el.addClass(this.validClass);
37065         } else {
37066             this.el.removeClass(['is-invalid','is-valid']);
37067             this.el.addClass(['is-valid']);
37068         }
37069         this.fireEvent('valid', this);
37070     },
37071     
37072     markInvalid : function(msg)
37073     {
37074         if(this.allowBlank || this.disabled){
37075             return;
37076         }
37077         
37078         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37079             this.indicatorEl().removeClass('invisible');
37080             this.indicatorEl().addClass('visible');
37081         }
37082         if (Roo.bootstrap.version == 3) {
37083             this.el.removeClass([this.invalidClass, this.validClass]);
37084             this.el.addClass(this.invalidClass);
37085         } else {
37086             this.el.removeClass(['is-invalid','is-valid']);
37087             this.el.addClass(['is-invalid']);
37088         }
37089         
37090         this.fireEvent('invalid', this, msg);
37091         
37092     },
37093     
37094     setValue : function(v, suppressEvent)
37095     {   
37096         if(this.value === v){
37097             return;
37098         }
37099         
37100         this.value = v;
37101         
37102         if(this.rendered){
37103             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37104         }
37105         
37106         Roo.each(this.radioes, function(i){
37107             i.checked = false;
37108             i.el.removeClass('checked');
37109         });
37110         
37111         Roo.each(this.radioes, function(i){
37112             
37113             if(i.value === v || i.value.toString() === v.toString()){
37114                 i.checked = true;
37115                 i.el.addClass('checked');
37116                 
37117                 if(suppressEvent !== true){
37118                     this.fireEvent('check', this, i);
37119                 }
37120                 
37121                 return false;
37122             }
37123             
37124         }, this);
37125         
37126         this.validate();
37127     },
37128     
37129     clearInvalid : function(){
37130         
37131         if(!this.el || this.preventMark){
37132             return;
37133         }
37134         
37135         this.el.removeClass([this.invalidClass]);
37136         
37137         this.fireEvent('valid', this);
37138     }
37139     
37140 });
37141
37142 Roo.apply(Roo.bootstrap.RadioSet, {
37143     
37144     groups: {},
37145     
37146     register : function(set)
37147     {
37148         this.groups[set.name] = set;
37149     },
37150     
37151     get: function(name) 
37152     {
37153         if (typeof(this.groups[name]) == 'undefined') {
37154             return false;
37155         }
37156         
37157         return this.groups[name] ;
37158     }
37159     
37160 });
37161 /*
37162  * Based on:
37163  * Ext JS Library 1.1.1
37164  * Copyright(c) 2006-2007, Ext JS, LLC.
37165  *
37166  * Originally Released Under LGPL - original licence link has changed is not relivant.
37167  *
37168  * Fork - LGPL
37169  * <script type="text/javascript">
37170  */
37171
37172
37173 /**
37174  * @class Roo.bootstrap.SplitBar
37175  * @extends Roo.util.Observable
37176  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37177  * <br><br>
37178  * Usage:
37179  * <pre><code>
37180 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37181                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37182 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37183 split.minSize = 100;
37184 split.maxSize = 600;
37185 split.animate = true;
37186 split.on('moved', splitterMoved);
37187 </code></pre>
37188  * @constructor
37189  * Create a new SplitBar
37190  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37191  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37192  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37193  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37194                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37195                         position of the SplitBar).
37196  */
37197 Roo.bootstrap.SplitBar = function(cfg){
37198     
37199     /** @private */
37200     
37201     //{
37202     //  dragElement : elm
37203     //  resizingElement: el,
37204         // optional..
37205     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37206     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37207         // existingProxy ???
37208     //}
37209     
37210     this.el = Roo.get(cfg.dragElement, true);
37211     this.el.dom.unselectable = "on";
37212     /** @private */
37213     this.resizingEl = Roo.get(cfg.resizingElement, true);
37214
37215     /**
37216      * @private
37217      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37218      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37219      * @type Number
37220      */
37221     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37222     
37223     /**
37224      * The minimum size of the resizing element. (Defaults to 0)
37225      * @type Number
37226      */
37227     this.minSize = 0;
37228     
37229     /**
37230      * The maximum size of the resizing element. (Defaults to 2000)
37231      * @type Number
37232      */
37233     this.maxSize = 2000;
37234     
37235     /**
37236      * Whether to animate the transition to the new size
37237      * @type Boolean
37238      */
37239     this.animate = false;
37240     
37241     /**
37242      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37243      * @type Boolean
37244      */
37245     this.useShim = false;
37246     
37247     /** @private */
37248     this.shim = null;
37249     
37250     if(!cfg.existingProxy){
37251         /** @private */
37252         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37253     }else{
37254         this.proxy = Roo.get(cfg.existingProxy).dom;
37255     }
37256     /** @private */
37257     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37258     
37259     /** @private */
37260     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37261     
37262     /** @private */
37263     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37264     
37265     /** @private */
37266     this.dragSpecs = {};
37267     
37268     /**
37269      * @private The adapter to use to positon and resize elements
37270      */
37271     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37272     this.adapter.init(this);
37273     
37274     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37275         /** @private */
37276         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37277         this.el.addClass("roo-splitbar-h");
37278     }else{
37279         /** @private */
37280         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37281         this.el.addClass("roo-splitbar-v");
37282     }
37283     
37284     this.addEvents({
37285         /**
37286          * @event resize
37287          * Fires when the splitter is moved (alias for {@link #event-moved})
37288          * @param {Roo.bootstrap.SplitBar} this
37289          * @param {Number} newSize the new width or height
37290          */
37291         "resize" : true,
37292         /**
37293          * @event moved
37294          * Fires when the splitter is moved
37295          * @param {Roo.bootstrap.SplitBar} this
37296          * @param {Number} newSize the new width or height
37297          */
37298         "moved" : true,
37299         /**
37300          * @event beforeresize
37301          * Fires before the splitter is dragged
37302          * @param {Roo.bootstrap.SplitBar} this
37303          */
37304         "beforeresize" : true,
37305
37306         "beforeapply" : true
37307     });
37308
37309     Roo.util.Observable.call(this);
37310 };
37311
37312 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37313     onStartProxyDrag : function(x, y){
37314         this.fireEvent("beforeresize", this);
37315         if(!this.overlay){
37316             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37317             o.unselectable();
37318             o.enableDisplayMode("block");
37319             // all splitbars share the same overlay
37320             Roo.bootstrap.SplitBar.prototype.overlay = o;
37321         }
37322         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37323         this.overlay.show();
37324         Roo.get(this.proxy).setDisplayed("block");
37325         var size = this.adapter.getElementSize(this);
37326         this.activeMinSize = this.getMinimumSize();;
37327         this.activeMaxSize = this.getMaximumSize();;
37328         var c1 = size - this.activeMinSize;
37329         var c2 = Math.max(this.activeMaxSize - size, 0);
37330         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37331             this.dd.resetConstraints();
37332             this.dd.setXConstraint(
37333                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37334                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37335             );
37336             this.dd.setYConstraint(0, 0);
37337         }else{
37338             this.dd.resetConstraints();
37339             this.dd.setXConstraint(0, 0);
37340             this.dd.setYConstraint(
37341                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37342                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37343             );
37344          }
37345         this.dragSpecs.startSize = size;
37346         this.dragSpecs.startPoint = [x, y];
37347         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37348     },
37349     
37350     /** 
37351      * @private Called after the drag operation by the DDProxy
37352      */
37353     onEndProxyDrag : function(e){
37354         Roo.get(this.proxy).setDisplayed(false);
37355         var endPoint = Roo.lib.Event.getXY(e);
37356         if(this.overlay){
37357             this.overlay.hide();
37358         }
37359         var newSize;
37360         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37361             newSize = this.dragSpecs.startSize + 
37362                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37363                     endPoint[0] - this.dragSpecs.startPoint[0] :
37364                     this.dragSpecs.startPoint[0] - endPoint[0]
37365                 );
37366         }else{
37367             newSize = this.dragSpecs.startSize + 
37368                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37369                     endPoint[1] - this.dragSpecs.startPoint[1] :
37370                     this.dragSpecs.startPoint[1] - endPoint[1]
37371                 );
37372         }
37373         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37374         if(newSize != this.dragSpecs.startSize){
37375             if(this.fireEvent('beforeapply', this, newSize) !== false){
37376                 this.adapter.setElementSize(this, newSize);
37377                 this.fireEvent("moved", this, newSize);
37378                 this.fireEvent("resize", this, newSize);
37379             }
37380         }
37381     },
37382     
37383     /**
37384      * Get the adapter this SplitBar uses
37385      * @return The adapter object
37386      */
37387     getAdapter : function(){
37388         return this.adapter;
37389     },
37390     
37391     /**
37392      * Set the adapter this SplitBar uses
37393      * @param {Object} adapter A SplitBar adapter object
37394      */
37395     setAdapter : function(adapter){
37396         this.adapter = adapter;
37397         this.adapter.init(this);
37398     },
37399     
37400     /**
37401      * Gets the minimum size for the resizing element
37402      * @return {Number} The minimum size
37403      */
37404     getMinimumSize : function(){
37405         return this.minSize;
37406     },
37407     
37408     /**
37409      * Sets the minimum size for the resizing element
37410      * @param {Number} minSize The minimum size
37411      */
37412     setMinimumSize : function(minSize){
37413         this.minSize = minSize;
37414     },
37415     
37416     /**
37417      * Gets the maximum size for the resizing element
37418      * @return {Number} The maximum size
37419      */
37420     getMaximumSize : function(){
37421         return this.maxSize;
37422     },
37423     
37424     /**
37425      * Sets the maximum size for the resizing element
37426      * @param {Number} maxSize The maximum size
37427      */
37428     setMaximumSize : function(maxSize){
37429         this.maxSize = maxSize;
37430     },
37431     
37432     /**
37433      * Sets the initialize size for the resizing element
37434      * @param {Number} size The initial size
37435      */
37436     setCurrentSize : function(size){
37437         var oldAnimate = this.animate;
37438         this.animate = false;
37439         this.adapter.setElementSize(this, size);
37440         this.animate = oldAnimate;
37441     },
37442     
37443     /**
37444      * Destroy this splitbar. 
37445      * @param {Boolean} removeEl True to remove the element
37446      */
37447     destroy : function(removeEl){
37448         if(this.shim){
37449             this.shim.remove();
37450         }
37451         this.dd.unreg();
37452         this.proxy.parentNode.removeChild(this.proxy);
37453         if(removeEl){
37454             this.el.remove();
37455         }
37456     }
37457 });
37458
37459 /**
37460  * @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.
37461  */
37462 Roo.bootstrap.SplitBar.createProxy = function(dir){
37463     var proxy = new Roo.Element(document.createElement("div"));
37464     proxy.unselectable();
37465     var cls = 'roo-splitbar-proxy';
37466     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37467     document.body.appendChild(proxy.dom);
37468     return proxy.dom;
37469 };
37470
37471 /** 
37472  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37473  * Default Adapter. It assumes the splitter and resizing element are not positioned
37474  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37475  */
37476 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37477 };
37478
37479 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37480     // do nothing for now
37481     init : function(s){
37482     
37483     },
37484     /**
37485      * Called before drag operations to get the current size of the resizing element. 
37486      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37487      */
37488      getElementSize : function(s){
37489         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37490             return s.resizingEl.getWidth();
37491         }else{
37492             return s.resizingEl.getHeight();
37493         }
37494     },
37495     
37496     /**
37497      * Called after drag operations to set the size of the resizing element.
37498      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37499      * @param {Number} newSize The new size to set
37500      * @param {Function} onComplete A function to be invoked when resizing is complete
37501      */
37502     setElementSize : function(s, newSize, onComplete){
37503         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37504             if(!s.animate){
37505                 s.resizingEl.setWidth(newSize);
37506                 if(onComplete){
37507                     onComplete(s, newSize);
37508                 }
37509             }else{
37510                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37511             }
37512         }else{
37513             
37514             if(!s.animate){
37515                 s.resizingEl.setHeight(newSize);
37516                 if(onComplete){
37517                     onComplete(s, newSize);
37518                 }
37519             }else{
37520                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37521             }
37522         }
37523     }
37524 };
37525
37526 /** 
37527  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37528  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37529  * Adapter that  moves the splitter element to align with the resized sizing element. 
37530  * Used with an absolute positioned SplitBar.
37531  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37532  * document.body, make sure you assign an id to the body element.
37533  */
37534 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37535     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37536     this.container = Roo.get(container);
37537 };
37538
37539 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37540     init : function(s){
37541         this.basic.init(s);
37542     },
37543     
37544     getElementSize : function(s){
37545         return this.basic.getElementSize(s);
37546     },
37547     
37548     setElementSize : function(s, newSize, onComplete){
37549         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37550     },
37551     
37552     moveSplitter : function(s){
37553         var yes = Roo.bootstrap.SplitBar;
37554         switch(s.placement){
37555             case yes.LEFT:
37556                 s.el.setX(s.resizingEl.getRight());
37557                 break;
37558             case yes.RIGHT:
37559                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37560                 break;
37561             case yes.TOP:
37562                 s.el.setY(s.resizingEl.getBottom());
37563                 break;
37564             case yes.BOTTOM:
37565                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37566                 break;
37567         }
37568     }
37569 };
37570
37571 /**
37572  * Orientation constant - Create a vertical SplitBar
37573  * @static
37574  * @type Number
37575  */
37576 Roo.bootstrap.SplitBar.VERTICAL = 1;
37577
37578 /**
37579  * Orientation constant - Create a horizontal SplitBar
37580  * @static
37581  * @type Number
37582  */
37583 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37584
37585 /**
37586  * Placement constant - The resizing element is to the left of the splitter element
37587  * @static
37588  * @type Number
37589  */
37590 Roo.bootstrap.SplitBar.LEFT = 1;
37591
37592 /**
37593  * Placement constant - The resizing element is to the right of the splitter element
37594  * @static
37595  * @type Number
37596  */
37597 Roo.bootstrap.SplitBar.RIGHT = 2;
37598
37599 /**
37600  * Placement constant - The resizing element is positioned above the splitter element
37601  * @static
37602  * @type Number
37603  */
37604 Roo.bootstrap.SplitBar.TOP = 3;
37605
37606 /**
37607  * Placement constant - The resizing element is positioned under splitter element
37608  * @static
37609  * @type Number
37610  */
37611 Roo.bootstrap.SplitBar.BOTTOM = 4;
37612 Roo.namespace("Roo.bootstrap.layout");/*
37613  * Based on:
37614  * Ext JS Library 1.1.1
37615  * Copyright(c) 2006-2007, Ext JS, LLC.
37616  *
37617  * Originally Released Under LGPL - original licence link has changed is not relivant.
37618  *
37619  * Fork - LGPL
37620  * <script type="text/javascript">
37621  */
37622
37623 /**
37624  * @class Roo.bootstrap.layout.Manager
37625  * @extends Roo.bootstrap.Component
37626  * Base class for layout managers.
37627  */
37628 Roo.bootstrap.layout.Manager = function(config)
37629 {
37630     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37631
37632
37633
37634
37635
37636     /** false to disable window resize monitoring @type Boolean */
37637     this.monitorWindowResize = true;
37638     this.regions = {};
37639     this.addEvents({
37640         /**
37641          * @event layout
37642          * Fires when a layout is performed.
37643          * @param {Roo.LayoutManager} this
37644          */
37645         "layout" : true,
37646         /**
37647          * @event regionresized
37648          * Fires when the user resizes a region.
37649          * @param {Roo.LayoutRegion} region The resized region
37650          * @param {Number} newSize The new size (width for east/west, height for north/south)
37651          */
37652         "regionresized" : true,
37653         /**
37654          * @event regioncollapsed
37655          * Fires when a region is collapsed.
37656          * @param {Roo.LayoutRegion} region The collapsed region
37657          */
37658         "regioncollapsed" : true,
37659         /**
37660          * @event regionexpanded
37661          * Fires when a region is expanded.
37662          * @param {Roo.LayoutRegion} region The expanded region
37663          */
37664         "regionexpanded" : true
37665     });
37666     this.updating = false;
37667
37668     if (config.el) {
37669         this.el = Roo.get(config.el);
37670         this.initEvents();
37671     }
37672
37673 };
37674
37675 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37676
37677
37678     regions : null,
37679
37680     monitorWindowResize : true,
37681
37682
37683     updating : false,
37684
37685
37686     onRender : function(ct, position)
37687     {
37688         if(!this.el){
37689             this.el = Roo.get(ct);
37690             this.initEvents();
37691         }
37692         //this.fireEvent('render',this);
37693     },
37694
37695
37696     initEvents: function()
37697     {
37698
37699
37700         // ie scrollbar fix
37701         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37702             document.body.scroll = "no";
37703         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37704             this.el.position('relative');
37705         }
37706         this.id = this.el.id;
37707         this.el.addClass("roo-layout-container");
37708         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37709         if(this.el.dom != document.body ) {
37710             this.el.on('resize', this.layout,this);
37711             this.el.on('show', this.layout,this);
37712         }
37713
37714     },
37715
37716     /**
37717      * Returns true if this layout is currently being updated
37718      * @return {Boolean}
37719      */
37720     isUpdating : function(){
37721         return this.updating;
37722     },
37723
37724     /**
37725      * Suspend the LayoutManager from doing auto-layouts while
37726      * making multiple add or remove calls
37727      */
37728     beginUpdate : function(){
37729         this.updating = true;
37730     },
37731
37732     /**
37733      * Restore auto-layouts and optionally disable the manager from performing a layout
37734      * @param {Boolean} noLayout true to disable a layout update
37735      */
37736     endUpdate : function(noLayout){
37737         this.updating = false;
37738         if(!noLayout){
37739             this.layout();
37740         }
37741     },
37742
37743     layout: function(){
37744         // abstract...
37745     },
37746
37747     onRegionResized : function(region, newSize){
37748         this.fireEvent("regionresized", region, newSize);
37749         this.layout();
37750     },
37751
37752     onRegionCollapsed : function(region){
37753         this.fireEvent("regioncollapsed", region);
37754     },
37755
37756     onRegionExpanded : function(region){
37757         this.fireEvent("regionexpanded", region);
37758     },
37759
37760     /**
37761      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37762      * performs box-model adjustments.
37763      * @return {Object} The size as an object {width: (the width), height: (the height)}
37764      */
37765     getViewSize : function()
37766     {
37767         var size;
37768         if(this.el.dom != document.body){
37769             size = this.el.getSize();
37770         }else{
37771             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37772         }
37773         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37774         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37775         return size;
37776     },
37777
37778     /**
37779      * Returns the Element this layout is bound to.
37780      * @return {Roo.Element}
37781      */
37782     getEl : function(){
37783         return this.el;
37784     },
37785
37786     /**
37787      * Returns the specified region.
37788      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37789      * @return {Roo.LayoutRegion}
37790      */
37791     getRegion : function(target){
37792         return this.regions[target.toLowerCase()];
37793     },
37794
37795     onWindowResize : function(){
37796         if(this.monitorWindowResize){
37797             this.layout();
37798         }
37799     }
37800 });
37801 /*
37802  * Based on:
37803  * Ext JS Library 1.1.1
37804  * Copyright(c) 2006-2007, Ext JS, LLC.
37805  *
37806  * Originally Released Under LGPL - original licence link has changed is not relivant.
37807  *
37808  * Fork - LGPL
37809  * <script type="text/javascript">
37810  */
37811 /**
37812  * @class Roo.bootstrap.layout.Border
37813  * @extends Roo.bootstrap.layout.Manager
37814  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37815  * please see: examples/bootstrap/nested.html<br><br>
37816  
37817 <b>The container the layout is rendered into can be either the body element or any other element.
37818 If it is not the body element, the container needs to either be an absolute positioned element,
37819 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37820 the container size if it is not the body element.</b>
37821
37822 * @constructor
37823 * Create a new Border
37824 * @param {Object} config Configuration options
37825  */
37826 Roo.bootstrap.layout.Border = function(config){
37827     config = config || {};
37828     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37829     
37830     
37831     
37832     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37833         if(config[region]){
37834             config[region].region = region;
37835             this.addRegion(config[region]);
37836         }
37837     },this);
37838     
37839 };
37840
37841 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37842
37843 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37844     
37845     parent : false, // this might point to a 'nest' or a ???
37846     
37847     /**
37848      * Creates and adds a new region if it doesn't already exist.
37849      * @param {String} target The target region key (north, south, east, west or center).
37850      * @param {Object} config The regions config object
37851      * @return {BorderLayoutRegion} The new region
37852      */
37853     addRegion : function(config)
37854     {
37855         if(!this.regions[config.region]){
37856             var r = this.factory(config);
37857             this.bindRegion(r);
37858         }
37859         return this.regions[config.region];
37860     },
37861
37862     // private (kinda)
37863     bindRegion : function(r){
37864         this.regions[r.config.region] = r;
37865         
37866         r.on("visibilitychange",    this.layout, this);
37867         r.on("paneladded",          this.layout, this);
37868         r.on("panelremoved",        this.layout, this);
37869         r.on("invalidated",         this.layout, this);
37870         r.on("resized",             this.onRegionResized, this);
37871         r.on("collapsed",           this.onRegionCollapsed, this);
37872         r.on("expanded",            this.onRegionExpanded, this);
37873     },
37874
37875     /**
37876      * Performs a layout update.
37877      */
37878     layout : function()
37879     {
37880         if(this.updating) {
37881             return;
37882         }
37883         
37884         // render all the rebions if they have not been done alreayd?
37885         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37886             if(this.regions[region] && !this.regions[region].bodyEl){
37887                 this.regions[region].onRender(this.el)
37888             }
37889         },this);
37890         
37891         var size = this.getViewSize();
37892         var w = size.width;
37893         var h = size.height;
37894         var centerW = w;
37895         var centerH = h;
37896         var centerY = 0;
37897         var centerX = 0;
37898         //var x = 0, y = 0;
37899
37900         var rs = this.regions;
37901         var north = rs["north"];
37902         var south = rs["south"]; 
37903         var west = rs["west"];
37904         var east = rs["east"];
37905         var center = rs["center"];
37906         //if(this.hideOnLayout){ // not supported anymore
37907             //c.el.setStyle("display", "none");
37908         //}
37909         if(north && north.isVisible()){
37910             var b = north.getBox();
37911             var m = north.getMargins();
37912             b.width = w - (m.left+m.right);
37913             b.x = m.left;
37914             b.y = m.top;
37915             centerY = b.height + b.y + m.bottom;
37916             centerH -= centerY;
37917             north.updateBox(this.safeBox(b));
37918         }
37919         if(south && south.isVisible()){
37920             var b = south.getBox();
37921             var m = south.getMargins();
37922             b.width = w - (m.left+m.right);
37923             b.x = m.left;
37924             var totalHeight = (b.height + m.top + m.bottom);
37925             b.y = h - totalHeight + m.top;
37926             centerH -= totalHeight;
37927             south.updateBox(this.safeBox(b));
37928         }
37929         if(west && west.isVisible()){
37930             var b = west.getBox();
37931             var m = west.getMargins();
37932             b.height = centerH - (m.top+m.bottom);
37933             b.x = m.left;
37934             b.y = centerY + m.top;
37935             var totalWidth = (b.width + m.left + m.right);
37936             centerX += totalWidth;
37937             centerW -= totalWidth;
37938             west.updateBox(this.safeBox(b));
37939         }
37940         if(east && east.isVisible()){
37941             var b = east.getBox();
37942             var m = east.getMargins();
37943             b.height = centerH - (m.top+m.bottom);
37944             var totalWidth = (b.width + m.left + m.right);
37945             b.x = w - totalWidth + m.left;
37946             b.y = centerY + m.top;
37947             centerW -= totalWidth;
37948             east.updateBox(this.safeBox(b));
37949         }
37950         if(center){
37951             var m = center.getMargins();
37952             var centerBox = {
37953                 x: centerX + m.left,
37954                 y: centerY + m.top,
37955                 width: centerW - (m.left+m.right),
37956                 height: centerH - (m.top+m.bottom)
37957             };
37958             //if(this.hideOnLayout){
37959                 //center.el.setStyle("display", "block");
37960             //}
37961             center.updateBox(this.safeBox(centerBox));
37962         }
37963         this.el.repaint();
37964         this.fireEvent("layout", this);
37965     },
37966
37967     // private
37968     safeBox : function(box){
37969         box.width = Math.max(0, box.width);
37970         box.height = Math.max(0, box.height);
37971         return box;
37972     },
37973
37974     /**
37975      * Adds a ContentPanel (or subclass) to this layout.
37976      * @param {String} target The target region key (north, south, east, west or center).
37977      * @param {Roo.ContentPanel} panel The panel to add
37978      * @return {Roo.ContentPanel} The added panel
37979      */
37980     add : function(target, panel){
37981          
37982         target = target.toLowerCase();
37983         return this.regions[target].add(panel);
37984     },
37985
37986     /**
37987      * Remove a ContentPanel (or subclass) to this layout.
37988      * @param {String} target The target region key (north, south, east, west or center).
37989      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37990      * @return {Roo.ContentPanel} The removed panel
37991      */
37992     remove : function(target, panel){
37993         target = target.toLowerCase();
37994         return this.regions[target].remove(panel);
37995     },
37996
37997     /**
37998      * Searches all regions for a panel with the specified id
37999      * @param {String} panelId
38000      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38001      */
38002     findPanel : function(panelId){
38003         var rs = this.regions;
38004         for(var target in rs){
38005             if(typeof rs[target] != "function"){
38006                 var p = rs[target].getPanel(panelId);
38007                 if(p){
38008                     return p;
38009                 }
38010             }
38011         }
38012         return null;
38013     },
38014
38015     /**
38016      * Searches all regions for a panel with the specified id and activates (shows) it.
38017      * @param {String/ContentPanel} panelId The panels id or the panel itself
38018      * @return {Roo.ContentPanel} The shown panel or null
38019      */
38020     showPanel : function(panelId) {
38021       var rs = this.regions;
38022       for(var target in rs){
38023          var r = rs[target];
38024          if(typeof r != "function"){
38025             if(r.hasPanel(panelId)){
38026                return r.showPanel(panelId);
38027             }
38028          }
38029       }
38030       return null;
38031    },
38032
38033    /**
38034      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38035      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38036      */
38037    /*
38038     restoreState : function(provider){
38039         if(!provider){
38040             provider = Roo.state.Manager;
38041         }
38042         var sm = new Roo.LayoutStateManager();
38043         sm.init(this, provider);
38044     },
38045 */
38046  
38047  
38048     /**
38049      * Adds a xtype elements to the layout.
38050      * <pre><code>
38051
38052 layout.addxtype({
38053        xtype : 'ContentPanel',
38054        region: 'west',
38055        items: [ .... ]
38056    }
38057 );
38058
38059 layout.addxtype({
38060         xtype : 'NestedLayoutPanel',
38061         region: 'west',
38062         layout: {
38063            center: { },
38064            west: { }   
38065         },
38066         items : [ ... list of content panels or nested layout panels.. ]
38067    }
38068 );
38069 </code></pre>
38070      * @param {Object} cfg Xtype definition of item to add.
38071      */
38072     addxtype : function(cfg)
38073     {
38074         // basically accepts a pannel...
38075         // can accept a layout region..!?!?
38076         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38077         
38078         
38079         // theory?  children can only be panels??
38080         
38081         //if (!cfg.xtype.match(/Panel$/)) {
38082         //    return false;
38083         //}
38084         var ret = false;
38085         
38086         if (typeof(cfg.region) == 'undefined') {
38087             Roo.log("Failed to add Panel, region was not set");
38088             Roo.log(cfg);
38089             return false;
38090         }
38091         var region = cfg.region;
38092         delete cfg.region;
38093         
38094           
38095         var xitems = [];
38096         if (cfg.items) {
38097             xitems = cfg.items;
38098             delete cfg.items;
38099         }
38100         var nb = false;
38101         
38102         if ( region == 'center') {
38103             Roo.log("Center: " + cfg.title);
38104         }
38105         
38106         
38107         switch(cfg.xtype) 
38108         {
38109             case 'Content':  // ContentPanel (el, cfg)
38110             case 'Scroll':  // ContentPanel (el, cfg)
38111             case 'View': 
38112                 cfg.autoCreate = cfg.autoCreate || true;
38113                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38114                 //} else {
38115                 //    var el = this.el.createChild();
38116                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38117                 //}
38118                 
38119                 this.add(region, ret);
38120                 break;
38121             
38122             /*
38123             case 'TreePanel': // our new panel!
38124                 cfg.el = this.el.createChild();
38125                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38126                 this.add(region, ret);
38127                 break;
38128             */
38129             
38130             case 'Nest': 
38131                 // create a new Layout (which is  a Border Layout...
38132                 
38133                 var clayout = cfg.layout;
38134                 clayout.el  = this.el.createChild();
38135                 clayout.items   = clayout.items  || [];
38136                 
38137                 delete cfg.layout;
38138                 
38139                 // replace this exitems with the clayout ones..
38140                 xitems = clayout.items;
38141                  
38142                 // force background off if it's in center...
38143                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38144                     cfg.background = false;
38145                 }
38146                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38147                 
38148                 
38149                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38150                 //console.log('adding nested layout panel '  + cfg.toSource());
38151                 this.add(region, ret);
38152                 nb = {}; /// find first...
38153                 break;
38154             
38155             case 'Grid':
38156                 
38157                 // needs grid and region
38158                 
38159                 //var el = this.getRegion(region).el.createChild();
38160                 /*
38161                  *var el = this.el.createChild();
38162                 // create the grid first...
38163                 cfg.grid.container = el;
38164                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38165                 */
38166                 
38167                 if (region == 'center' && this.active ) {
38168                     cfg.background = false;
38169                 }
38170                 
38171                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38172                 
38173                 this.add(region, ret);
38174                 /*
38175                 if (cfg.background) {
38176                     // render grid on panel activation (if panel background)
38177                     ret.on('activate', function(gp) {
38178                         if (!gp.grid.rendered) {
38179                     //        gp.grid.render(el);
38180                         }
38181                     });
38182                 } else {
38183                   //  cfg.grid.render(el);
38184                 }
38185                 */
38186                 break;
38187            
38188            
38189             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38190                 // it was the old xcomponent building that caused this before.
38191                 // espeically if border is the top element in the tree.
38192                 ret = this;
38193                 break; 
38194                 
38195                     
38196                 
38197                 
38198                 
38199             default:
38200                 /*
38201                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38202                     
38203                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38204                     this.add(region, ret);
38205                 } else {
38206                 */
38207                     Roo.log(cfg);
38208                     throw "Can not add '" + cfg.xtype + "' to Border";
38209                     return null;
38210              
38211                                 
38212              
38213         }
38214         this.beginUpdate();
38215         // add children..
38216         var region = '';
38217         var abn = {};
38218         Roo.each(xitems, function(i)  {
38219             region = nb && i.region ? i.region : false;
38220             
38221             var add = ret.addxtype(i);
38222            
38223             if (region) {
38224                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38225                 if (!i.background) {
38226                     abn[region] = nb[region] ;
38227                 }
38228             }
38229             
38230         });
38231         this.endUpdate();
38232
38233         // make the last non-background panel active..
38234         //if (nb) { Roo.log(abn); }
38235         if (nb) {
38236             
38237             for(var r in abn) {
38238                 region = this.getRegion(r);
38239                 if (region) {
38240                     // tried using nb[r], but it does not work..
38241                      
38242                     region.showPanel(abn[r]);
38243                    
38244                 }
38245             }
38246         }
38247         return ret;
38248         
38249     },
38250     
38251     
38252 // private
38253     factory : function(cfg)
38254     {
38255         
38256         var validRegions = Roo.bootstrap.layout.Border.regions;
38257
38258         var target = cfg.region;
38259         cfg.mgr = this;
38260         
38261         var r = Roo.bootstrap.layout;
38262         Roo.log(target);
38263         switch(target){
38264             case "north":
38265                 return new r.North(cfg);
38266             case "south":
38267                 return new r.South(cfg);
38268             case "east":
38269                 return new r.East(cfg);
38270             case "west":
38271                 return new r.West(cfg);
38272             case "center":
38273                 return new r.Center(cfg);
38274         }
38275         throw 'Layout region "'+target+'" not supported.';
38276     }
38277     
38278     
38279 });
38280  /*
38281  * Based on:
38282  * Ext JS Library 1.1.1
38283  * Copyright(c) 2006-2007, Ext JS, LLC.
38284  *
38285  * Originally Released Under LGPL - original licence link has changed is not relivant.
38286  *
38287  * Fork - LGPL
38288  * <script type="text/javascript">
38289  */
38290  
38291 /**
38292  * @class Roo.bootstrap.layout.Basic
38293  * @extends Roo.util.Observable
38294  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38295  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38296  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38297  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38298  * @cfg {string}   region  the region that it inhabits..
38299  * @cfg {bool}   skipConfig skip config?
38300  * 
38301
38302  */
38303 Roo.bootstrap.layout.Basic = function(config){
38304     
38305     this.mgr = config.mgr;
38306     
38307     this.position = config.region;
38308     
38309     var skipConfig = config.skipConfig;
38310     
38311     this.events = {
38312         /**
38313          * @scope Roo.BasicLayoutRegion
38314          */
38315         
38316         /**
38317          * @event beforeremove
38318          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38319          * @param {Roo.LayoutRegion} this
38320          * @param {Roo.ContentPanel} panel The panel
38321          * @param {Object} e The cancel event object
38322          */
38323         "beforeremove" : true,
38324         /**
38325          * @event invalidated
38326          * Fires when the layout for this region is changed.
38327          * @param {Roo.LayoutRegion} this
38328          */
38329         "invalidated" : true,
38330         /**
38331          * @event visibilitychange
38332          * Fires when this region is shown or hidden 
38333          * @param {Roo.LayoutRegion} this
38334          * @param {Boolean} visibility true or false
38335          */
38336         "visibilitychange" : true,
38337         /**
38338          * @event paneladded
38339          * Fires when a panel is added. 
38340          * @param {Roo.LayoutRegion} this
38341          * @param {Roo.ContentPanel} panel The panel
38342          */
38343         "paneladded" : true,
38344         /**
38345          * @event panelremoved
38346          * Fires when a panel is removed. 
38347          * @param {Roo.LayoutRegion} this
38348          * @param {Roo.ContentPanel} panel The panel
38349          */
38350         "panelremoved" : true,
38351         /**
38352          * @event beforecollapse
38353          * Fires when this region before collapse.
38354          * @param {Roo.LayoutRegion} this
38355          */
38356         "beforecollapse" : true,
38357         /**
38358          * @event collapsed
38359          * Fires when this region is collapsed.
38360          * @param {Roo.LayoutRegion} this
38361          */
38362         "collapsed" : true,
38363         /**
38364          * @event expanded
38365          * Fires when this region is expanded.
38366          * @param {Roo.LayoutRegion} this
38367          */
38368         "expanded" : true,
38369         /**
38370          * @event slideshow
38371          * Fires when this region is slid into view.
38372          * @param {Roo.LayoutRegion} this
38373          */
38374         "slideshow" : true,
38375         /**
38376          * @event slidehide
38377          * Fires when this region slides out of view. 
38378          * @param {Roo.LayoutRegion} this
38379          */
38380         "slidehide" : true,
38381         /**
38382          * @event panelactivated
38383          * Fires when a panel is activated. 
38384          * @param {Roo.LayoutRegion} this
38385          * @param {Roo.ContentPanel} panel The activated panel
38386          */
38387         "panelactivated" : true,
38388         /**
38389          * @event resized
38390          * Fires when the user resizes this region. 
38391          * @param {Roo.LayoutRegion} this
38392          * @param {Number} newSize The new size (width for east/west, height for north/south)
38393          */
38394         "resized" : true
38395     };
38396     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38397     this.panels = new Roo.util.MixedCollection();
38398     this.panels.getKey = this.getPanelId.createDelegate(this);
38399     this.box = null;
38400     this.activePanel = null;
38401     // ensure listeners are added...
38402     
38403     if (config.listeners || config.events) {
38404         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38405             listeners : config.listeners || {},
38406             events : config.events || {}
38407         });
38408     }
38409     
38410     if(skipConfig !== true){
38411         this.applyConfig(config);
38412     }
38413 };
38414
38415 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38416 {
38417     getPanelId : function(p){
38418         return p.getId();
38419     },
38420     
38421     applyConfig : function(config){
38422         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38423         this.config = config;
38424         
38425     },
38426     
38427     /**
38428      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38429      * the width, for horizontal (north, south) the height.
38430      * @param {Number} newSize The new width or height
38431      */
38432     resizeTo : function(newSize){
38433         var el = this.el ? this.el :
38434                  (this.activePanel ? this.activePanel.getEl() : null);
38435         if(el){
38436             switch(this.position){
38437                 case "east":
38438                 case "west":
38439                     el.setWidth(newSize);
38440                     this.fireEvent("resized", this, newSize);
38441                 break;
38442                 case "north":
38443                 case "south":
38444                     el.setHeight(newSize);
38445                     this.fireEvent("resized", this, newSize);
38446                 break;                
38447             }
38448         }
38449     },
38450     
38451     getBox : function(){
38452         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38453     },
38454     
38455     getMargins : function(){
38456         return this.margins;
38457     },
38458     
38459     updateBox : function(box){
38460         this.box = box;
38461         var el = this.activePanel.getEl();
38462         el.dom.style.left = box.x + "px";
38463         el.dom.style.top = box.y + "px";
38464         this.activePanel.setSize(box.width, box.height);
38465     },
38466     
38467     /**
38468      * Returns the container element for this region.
38469      * @return {Roo.Element}
38470      */
38471     getEl : function(){
38472         return this.activePanel;
38473     },
38474     
38475     /**
38476      * Returns true if this region is currently visible.
38477      * @return {Boolean}
38478      */
38479     isVisible : function(){
38480         return this.activePanel ? true : false;
38481     },
38482     
38483     setActivePanel : function(panel){
38484         panel = this.getPanel(panel);
38485         if(this.activePanel && this.activePanel != panel){
38486             this.activePanel.setActiveState(false);
38487             this.activePanel.getEl().setLeftTop(-10000,-10000);
38488         }
38489         this.activePanel = panel;
38490         panel.setActiveState(true);
38491         if(this.box){
38492             panel.setSize(this.box.width, this.box.height);
38493         }
38494         this.fireEvent("panelactivated", this, panel);
38495         this.fireEvent("invalidated");
38496     },
38497     
38498     /**
38499      * Show the specified panel.
38500      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38501      * @return {Roo.ContentPanel} The shown panel or null
38502      */
38503     showPanel : function(panel){
38504         panel = this.getPanel(panel);
38505         if(panel){
38506             this.setActivePanel(panel);
38507         }
38508         return panel;
38509     },
38510     
38511     /**
38512      * Get the active panel for this region.
38513      * @return {Roo.ContentPanel} The active panel or null
38514      */
38515     getActivePanel : function(){
38516         return this.activePanel;
38517     },
38518     
38519     /**
38520      * Add the passed ContentPanel(s)
38521      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38522      * @return {Roo.ContentPanel} The panel added (if only one was added)
38523      */
38524     add : function(panel){
38525         if(arguments.length > 1){
38526             for(var i = 0, len = arguments.length; i < len; i++) {
38527                 this.add(arguments[i]);
38528             }
38529             return null;
38530         }
38531         if(this.hasPanel(panel)){
38532             this.showPanel(panel);
38533             return panel;
38534         }
38535         var el = panel.getEl();
38536         if(el.dom.parentNode != this.mgr.el.dom){
38537             this.mgr.el.dom.appendChild(el.dom);
38538         }
38539         if(panel.setRegion){
38540             panel.setRegion(this);
38541         }
38542         this.panels.add(panel);
38543         el.setStyle("position", "absolute");
38544         if(!panel.background){
38545             this.setActivePanel(panel);
38546             if(this.config.initialSize && this.panels.getCount()==1){
38547                 this.resizeTo(this.config.initialSize);
38548             }
38549         }
38550         this.fireEvent("paneladded", this, panel);
38551         return panel;
38552     },
38553     
38554     /**
38555      * Returns true if the panel is in this region.
38556      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38557      * @return {Boolean}
38558      */
38559     hasPanel : function(panel){
38560         if(typeof panel == "object"){ // must be panel obj
38561             panel = panel.getId();
38562         }
38563         return this.getPanel(panel) ? true : false;
38564     },
38565     
38566     /**
38567      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38568      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38569      * @param {Boolean} preservePanel Overrides the config preservePanel option
38570      * @return {Roo.ContentPanel} The panel that was removed
38571      */
38572     remove : function(panel, preservePanel){
38573         panel = this.getPanel(panel);
38574         if(!panel){
38575             return null;
38576         }
38577         var e = {};
38578         this.fireEvent("beforeremove", this, panel, e);
38579         if(e.cancel === true){
38580             return null;
38581         }
38582         var panelId = panel.getId();
38583         this.panels.removeKey(panelId);
38584         return panel;
38585     },
38586     
38587     /**
38588      * Returns the panel specified or null if it's not in this region.
38589      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38590      * @return {Roo.ContentPanel}
38591      */
38592     getPanel : function(id){
38593         if(typeof id == "object"){ // must be panel obj
38594             return id;
38595         }
38596         return this.panels.get(id);
38597     },
38598     
38599     /**
38600      * Returns this regions position (north/south/east/west/center).
38601      * @return {String} 
38602      */
38603     getPosition: function(){
38604         return this.position;    
38605     }
38606 });/*
38607  * Based on:
38608  * Ext JS Library 1.1.1
38609  * Copyright(c) 2006-2007, Ext JS, LLC.
38610  *
38611  * Originally Released Under LGPL - original licence link has changed is not relivant.
38612  *
38613  * Fork - LGPL
38614  * <script type="text/javascript">
38615  */
38616  
38617 /**
38618  * @class Roo.bootstrap.layout.Region
38619  * @extends Roo.bootstrap.layout.Basic
38620  * This class represents a region in a layout manager.
38621  
38622  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38623  * @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})
38624  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38625  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38626  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38627  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38628  * @cfg {String}    title           The title for the region (overrides panel titles)
38629  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38630  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38631  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38632  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38633  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38634  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38635  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38636  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38637  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38638  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38639
38640  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38641  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38642  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38643  * @cfg {Number}    width           For East/West panels
38644  * @cfg {Number}    height          For North/South panels
38645  * @cfg {Boolean}   split           To show the splitter
38646  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38647  * 
38648  * @cfg {string}   cls             Extra CSS classes to add to region
38649  * 
38650  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38651  * @cfg {string}   region  the region that it inhabits..
38652  *
38653
38654  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38655  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38656
38657  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38658  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38659  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38660  */
38661 Roo.bootstrap.layout.Region = function(config)
38662 {
38663     this.applyConfig(config);
38664
38665     var mgr = config.mgr;
38666     var pos = config.region;
38667     config.skipConfig = true;
38668     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38669     
38670     if (mgr.el) {
38671         this.onRender(mgr.el);   
38672     }
38673      
38674     this.visible = true;
38675     this.collapsed = false;
38676     this.unrendered_panels = [];
38677 };
38678
38679 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38680
38681     position: '', // set by wrapper (eg. north/south etc..)
38682     unrendered_panels : null,  // unrendered panels.
38683     
38684     tabPosition : false,
38685     
38686     mgr: false, // points to 'Border'
38687     
38688     
38689     createBody : function(){
38690         /** This region's body element 
38691         * @type Roo.Element */
38692         this.bodyEl = this.el.createChild({
38693                 tag: "div",
38694                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38695         });
38696     },
38697
38698     onRender: function(ctr, pos)
38699     {
38700         var dh = Roo.DomHelper;
38701         /** This region's container element 
38702         * @type Roo.Element */
38703         this.el = dh.append(ctr.dom, {
38704                 tag: "div",
38705                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38706             }, true);
38707         /** This region's title element 
38708         * @type Roo.Element */
38709     
38710         this.titleEl = dh.append(this.el.dom,  {
38711                 tag: "div",
38712                 unselectable: "on",
38713                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38714                 children:[
38715                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38716                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38717                 ]
38718             }, true);
38719         
38720         this.titleEl.enableDisplayMode();
38721         /** This region's title text element 
38722         * @type HTMLElement */
38723         this.titleTextEl = this.titleEl.dom.firstChild;
38724         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38725         /*
38726         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38727         this.closeBtn.enableDisplayMode();
38728         this.closeBtn.on("click", this.closeClicked, this);
38729         this.closeBtn.hide();
38730     */
38731         this.createBody(this.config);
38732         if(this.config.hideWhenEmpty){
38733             this.hide();
38734             this.on("paneladded", this.validateVisibility, this);
38735             this.on("panelremoved", this.validateVisibility, this);
38736         }
38737         if(this.autoScroll){
38738             this.bodyEl.setStyle("overflow", "auto");
38739         }else{
38740             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38741         }
38742         //if(c.titlebar !== false){
38743             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38744                 this.titleEl.hide();
38745             }else{
38746                 this.titleEl.show();
38747                 if(this.config.title){
38748                     this.titleTextEl.innerHTML = this.config.title;
38749                 }
38750             }
38751         //}
38752         if(this.config.collapsed){
38753             this.collapse(true);
38754         }
38755         if(this.config.hidden){
38756             this.hide();
38757         }
38758         
38759         if (this.unrendered_panels && this.unrendered_panels.length) {
38760             for (var i =0;i< this.unrendered_panels.length; i++) {
38761                 this.add(this.unrendered_panels[i]);
38762             }
38763             this.unrendered_panels = null;
38764             
38765         }
38766         
38767     },
38768     
38769     applyConfig : function(c)
38770     {
38771         /*
38772          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38773             var dh = Roo.DomHelper;
38774             if(c.titlebar !== false){
38775                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38776                 this.collapseBtn.on("click", this.collapse, this);
38777                 this.collapseBtn.enableDisplayMode();
38778                 /*
38779                 if(c.showPin === true || this.showPin){
38780                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38781                     this.stickBtn.enableDisplayMode();
38782                     this.stickBtn.on("click", this.expand, this);
38783                     this.stickBtn.hide();
38784                 }
38785                 
38786             }
38787             */
38788             /** This region's collapsed element
38789             * @type Roo.Element */
38790             /*
38791              *
38792             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38793                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38794             ]}, true);
38795             
38796             if(c.floatable !== false){
38797                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38798                this.collapsedEl.on("click", this.collapseClick, this);
38799             }
38800
38801             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38802                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38803                    id: "message", unselectable: "on", style:{"float":"left"}});
38804                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38805              }
38806             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38807             this.expandBtn.on("click", this.expand, this);
38808             
38809         }
38810         
38811         if(this.collapseBtn){
38812             this.collapseBtn.setVisible(c.collapsible == true);
38813         }
38814         
38815         this.cmargins = c.cmargins || this.cmargins ||
38816                          (this.position == "west" || this.position == "east" ?
38817                              {top: 0, left: 2, right:2, bottom: 0} :
38818                              {top: 2, left: 0, right:0, bottom: 2});
38819         */
38820         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38821         
38822         
38823         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38824         
38825         this.autoScroll = c.autoScroll || false;
38826         
38827         
38828        
38829         
38830         this.duration = c.duration || .30;
38831         this.slideDuration = c.slideDuration || .45;
38832         this.config = c;
38833        
38834     },
38835     /**
38836      * Returns true if this region is currently visible.
38837      * @return {Boolean}
38838      */
38839     isVisible : function(){
38840         return this.visible;
38841     },
38842
38843     /**
38844      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38845      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38846      */
38847     //setCollapsedTitle : function(title){
38848     //    title = title || "&#160;";
38849      //   if(this.collapsedTitleTextEl){
38850       //      this.collapsedTitleTextEl.innerHTML = title;
38851        // }
38852     //},
38853
38854     getBox : function(){
38855         var b;
38856       //  if(!this.collapsed){
38857             b = this.el.getBox(false, true);
38858        // }else{
38859           //  b = this.collapsedEl.getBox(false, true);
38860         //}
38861         return b;
38862     },
38863
38864     getMargins : function(){
38865         return this.margins;
38866         //return this.collapsed ? this.cmargins : this.margins;
38867     },
38868 /*
38869     highlight : function(){
38870         this.el.addClass("x-layout-panel-dragover");
38871     },
38872
38873     unhighlight : function(){
38874         this.el.removeClass("x-layout-panel-dragover");
38875     },
38876 */
38877     updateBox : function(box)
38878     {
38879         if (!this.bodyEl) {
38880             return; // not rendered yet..
38881         }
38882         
38883         this.box = box;
38884         if(!this.collapsed){
38885             this.el.dom.style.left = box.x + "px";
38886             this.el.dom.style.top = box.y + "px";
38887             this.updateBody(box.width, box.height);
38888         }else{
38889             this.collapsedEl.dom.style.left = box.x + "px";
38890             this.collapsedEl.dom.style.top = box.y + "px";
38891             this.collapsedEl.setSize(box.width, box.height);
38892         }
38893         if(this.tabs){
38894             this.tabs.autoSizeTabs();
38895         }
38896     },
38897
38898     updateBody : function(w, h)
38899     {
38900         if(w !== null){
38901             this.el.setWidth(w);
38902             w -= this.el.getBorderWidth("rl");
38903             if(this.config.adjustments){
38904                 w += this.config.adjustments[0];
38905             }
38906         }
38907         if(h !== null && h > 0){
38908             this.el.setHeight(h);
38909             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38910             h -= this.el.getBorderWidth("tb");
38911             if(this.config.adjustments){
38912                 h += this.config.adjustments[1];
38913             }
38914             this.bodyEl.setHeight(h);
38915             if(this.tabs){
38916                 h = this.tabs.syncHeight(h);
38917             }
38918         }
38919         if(this.panelSize){
38920             w = w !== null ? w : this.panelSize.width;
38921             h = h !== null ? h : this.panelSize.height;
38922         }
38923         if(this.activePanel){
38924             var el = this.activePanel.getEl();
38925             w = w !== null ? w : el.getWidth();
38926             h = h !== null ? h : el.getHeight();
38927             this.panelSize = {width: w, height: h};
38928             this.activePanel.setSize(w, h);
38929         }
38930         if(Roo.isIE && this.tabs){
38931             this.tabs.el.repaint();
38932         }
38933     },
38934
38935     /**
38936      * Returns the container element for this region.
38937      * @return {Roo.Element}
38938      */
38939     getEl : function(){
38940         return this.el;
38941     },
38942
38943     /**
38944      * Hides this region.
38945      */
38946     hide : function(){
38947         //if(!this.collapsed){
38948             this.el.dom.style.left = "-2000px";
38949             this.el.hide();
38950         //}else{
38951          //   this.collapsedEl.dom.style.left = "-2000px";
38952          //   this.collapsedEl.hide();
38953        // }
38954         this.visible = false;
38955         this.fireEvent("visibilitychange", this, false);
38956     },
38957
38958     /**
38959      * Shows this region if it was previously hidden.
38960      */
38961     show : function(){
38962         //if(!this.collapsed){
38963             this.el.show();
38964         //}else{
38965         //    this.collapsedEl.show();
38966        // }
38967         this.visible = true;
38968         this.fireEvent("visibilitychange", this, true);
38969     },
38970 /*
38971     closeClicked : function(){
38972         if(this.activePanel){
38973             this.remove(this.activePanel);
38974         }
38975     },
38976
38977     collapseClick : function(e){
38978         if(this.isSlid){
38979            e.stopPropagation();
38980            this.slideIn();
38981         }else{
38982            e.stopPropagation();
38983            this.slideOut();
38984         }
38985     },
38986 */
38987     /**
38988      * Collapses this region.
38989      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38990      */
38991     /*
38992     collapse : function(skipAnim, skipCheck = false){
38993         if(this.collapsed) {
38994             return;
38995         }
38996         
38997         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38998             
38999             this.collapsed = true;
39000             if(this.split){
39001                 this.split.el.hide();
39002             }
39003             if(this.config.animate && skipAnim !== true){
39004                 this.fireEvent("invalidated", this);
39005                 this.animateCollapse();
39006             }else{
39007                 this.el.setLocation(-20000,-20000);
39008                 this.el.hide();
39009                 this.collapsedEl.show();
39010                 this.fireEvent("collapsed", this);
39011                 this.fireEvent("invalidated", this);
39012             }
39013         }
39014         
39015     },
39016 */
39017     animateCollapse : function(){
39018         // overridden
39019     },
39020
39021     /**
39022      * Expands this region if it was previously collapsed.
39023      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39024      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39025      */
39026     /*
39027     expand : function(e, skipAnim){
39028         if(e) {
39029             e.stopPropagation();
39030         }
39031         if(!this.collapsed || this.el.hasActiveFx()) {
39032             return;
39033         }
39034         if(this.isSlid){
39035             this.afterSlideIn();
39036             skipAnim = true;
39037         }
39038         this.collapsed = false;
39039         if(this.config.animate && skipAnim !== true){
39040             this.animateExpand();
39041         }else{
39042             this.el.show();
39043             if(this.split){
39044                 this.split.el.show();
39045             }
39046             this.collapsedEl.setLocation(-2000,-2000);
39047             this.collapsedEl.hide();
39048             this.fireEvent("invalidated", this);
39049             this.fireEvent("expanded", this);
39050         }
39051     },
39052 */
39053     animateExpand : function(){
39054         // overridden
39055     },
39056
39057     initTabs : function()
39058     {
39059         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39060         
39061         var ts = new Roo.bootstrap.panel.Tabs({
39062             el: this.bodyEl.dom,
39063             region : this,
39064             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39065             disableTooltips: this.config.disableTabTips,
39066             toolbar : this.config.toolbar
39067         });
39068         
39069         if(this.config.hideTabs){
39070             ts.stripWrap.setDisplayed(false);
39071         }
39072         this.tabs = ts;
39073         ts.resizeTabs = this.config.resizeTabs === true;
39074         ts.minTabWidth = this.config.minTabWidth || 40;
39075         ts.maxTabWidth = this.config.maxTabWidth || 250;
39076         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39077         ts.monitorResize = false;
39078         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39079         ts.bodyEl.addClass('roo-layout-tabs-body');
39080         this.panels.each(this.initPanelAsTab, this);
39081     },
39082
39083     initPanelAsTab : function(panel){
39084         var ti = this.tabs.addTab(
39085             panel.getEl().id,
39086             panel.getTitle(),
39087             null,
39088             this.config.closeOnTab && panel.isClosable(),
39089             panel.tpl
39090         );
39091         if(panel.tabTip !== undefined){
39092             ti.setTooltip(panel.tabTip);
39093         }
39094         ti.on("activate", function(){
39095               this.setActivePanel(panel);
39096         }, this);
39097         
39098         if(this.config.closeOnTab){
39099             ti.on("beforeclose", function(t, e){
39100                 e.cancel = true;
39101                 this.remove(panel);
39102             }, this);
39103         }
39104         
39105         panel.tabItem = ti;
39106         
39107         return ti;
39108     },
39109
39110     updatePanelTitle : function(panel, title)
39111     {
39112         if(this.activePanel == panel){
39113             this.updateTitle(title);
39114         }
39115         if(this.tabs){
39116             var ti = this.tabs.getTab(panel.getEl().id);
39117             ti.setText(title);
39118             if(panel.tabTip !== undefined){
39119                 ti.setTooltip(panel.tabTip);
39120             }
39121         }
39122     },
39123
39124     updateTitle : function(title){
39125         if(this.titleTextEl && !this.config.title){
39126             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39127         }
39128     },
39129
39130     setActivePanel : function(panel)
39131     {
39132         panel = this.getPanel(panel);
39133         if(this.activePanel && this.activePanel != panel){
39134             if(this.activePanel.setActiveState(false) === false){
39135                 return;
39136             }
39137         }
39138         this.activePanel = panel;
39139         panel.setActiveState(true);
39140         if(this.panelSize){
39141             panel.setSize(this.panelSize.width, this.panelSize.height);
39142         }
39143         if(this.closeBtn){
39144             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39145         }
39146         this.updateTitle(panel.getTitle());
39147         if(this.tabs){
39148             this.fireEvent("invalidated", this);
39149         }
39150         this.fireEvent("panelactivated", this, panel);
39151     },
39152
39153     /**
39154      * Shows the specified panel.
39155      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39156      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39157      */
39158     showPanel : function(panel)
39159     {
39160         panel = this.getPanel(panel);
39161         if(panel){
39162             if(this.tabs){
39163                 var tab = this.tabs.getTab(panel.getEl().id);
39164                 if(tab.isHidden()){
39165                     this.tabs.unhideTab(tab.id);
39166                 }
39167                 tab.activate();
39168             }else{
39169                 this.setActivePanel(panel);
39170             }
39171         }
39172         return panel;
39173     },
39174
39175     /**
39176      * Get the active panel for this region.
39177      * @return {Roo.ContentPanel} The active panel or null
39178      */
39179     getActivePanel : function(){
39180         return this.activePanel;
39181     },
39182
39183     validateVisibility : function(){
39184         if(this.panels.getCount() < 1){
39185             this.updateTitle("&#160;");
39186             this.closeBtn.hide();
39187             this.hide();
39188         }else{
39189             if(!this.isVisible()){
39190                 this.show();
39191             }
39192         }
39193     },
39194
39195     /**
39196      * Adds the passed ContentPanel(s) to this region.
39197      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39198      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39199      */
39200     add : function(panel)
39201     {
39202         if(arguments.length > 1){
39203             for(var i = 0, len = arguments.length; i < len; i++) {
39204                 this.add(arguments[i]);
39205             }
39206             return null;
39207         }
39208         
39209         // if we have not been rendered yet, then we can not really do much of this..
39210         if (!this.bodyEl) {
39211             this.unrendered_panels.push(panel);
39212             return panel;
39213         }
39214         
39215         
39216         
39217         
39218         if(this.hasPanel(panel)){
39219             this.showPanel(panel);
39220             return panel;
39221         }
39222         panel.setRegion(this);
39223         this.panels.add(panel);
39224        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39225             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39226             // and hide them... ???
39227             this.bodyEl.dom.appendChild(panel.getEl().dom);
39228             if(panel.background !== true){
39229                 this.setActivePanel(panel);
39230             }
39231             this.fireEvent("paneladded", this, panel);
39232             return panel;
39233         }
39234         */
39235         if(!this.tabs){
39236             this.initTabs();
39237         }else{
39238             this.initPanelAsTab(panel);
39239         }
39240         
39241         
39242         if(panel.background !== true){
39243             this.tabs.activate(panel.getEl().id);
39244         }
39245         this.fireEvent("paneladded", this, panel);
39246         return panel;
39247     },
39248
39249     /**
39250      * Hides the tab for the specified panel.
39251      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39252      */
39253     hidePanel : function(panel){
39254         if(this.tabs && (panel = this.getPanel(panel))){
39255             this.tabs.hideTab(panel.getEl().id);
39256         }
39257     },
39258
39259     /**
39260      * Unhides the tab for a previously hidden panel.
39261      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39262      */
39263     unhidePanel : function(panel){
39264         if(this.tabs && (panel = this.getPanel(panel))){
39265             this.tabs.unhideTab(panel.getEl().id);
39266         }
39267     },
39268
39269     clearPanels : function(){
39270         while(this.panels.getCount() > 0){
39271              this.remove(this.panels.first());
39272         }
39273     },
39274
39275     /**
39276      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39277      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39278      * @param {Boolean} preservePanel Overrides the config preservePanel option
39279      * @return {Roo.ContentPanel} The panel that was removed
39280      */
39281     remove : function(panel, preservePanel)
39282     {
39283         panel = this.getPanel(panel);
39284         if(!panel){
39285             return null;
39286         }
39287         var e = {};
39288         this.fireEvent("beforeremove", this, panel, e);
39289         if(e.cancel === true){
39290             return null;
39291         }
39292         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39293         var panelId = panel.getId();
39294         this.panels.removeKey(panelId);
39295         if(preservePanel){
39296             document.body.appendChild(panel.getEl().dom);
39297         }
39298         if(this.tabs){
39299             this.tabs.removeTab(panel.getEl().id);
39300         }else if (!preservePanel){
39301             this.bodyEl.dom.removeChild(panel.getEl().dom);
39302         }
39303         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39304             var p = this.panels.first();
39305             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39306             tempEl.appendChild(p.getEl().dom);
39307             this.bodyEl.update("");
39308             this.bodyEl.dom.appendChild(p.getEl().dom);
39309             tempEl = null;
39310             this.updateTitle(p.getTitle());
39311             this.tabs = null;
39312             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39313             this.setActivePanel(p);
39314         }
39315         panel.setRegion(null);
39316         if(this.activePanel == panel){
39317             this.activePanel = null;
39318         }
39319         if(this.config.autoDestroy !== false && preservePanel !== true){
39320             try{panel.destroy();}catch(e){}
39321         }
39322         this.fireEvent("panelremoved", this, panel);
39323         return panel;
39324     },
39325
39326     /**
39327      * Returns the TabPanel component used by this region
39328      * @return {Roo.TabPanel}
39329      */
39330     getTabs : function(){
39331         return this.tabs;
39332     },
39333
39334     createTool : function(parentEl, className){
39335         var btn = Roo.DomHelper.append(parentEl, {
39336             tag: "div",
39337             cls: "x-layout-tools-button",
39338             children: [ {
39339                 tag: "div",
39340                 cls: "roo-layout-tools-button-inner " + className,
39341                 html: "&#160;"
39342             }]
39343         }, true);
39344         btn.addClassOnOver("roo-layout-tools-button-over");
39345         return btn;
39346     }
39347 });/*
39348  * Based on:
39349  * Ext JS Library 1.1.1
39350  * Copyright(c) 2006-2007, Ext JS, LLC.
39351  *
39352  * Originally Released Under LGPL - original licence link has changed is not relivant.
39353  *
39354  * Fork - LGPL
39355  * <script type="text/javascript">
39356  */
39357  
39358
39359
39360 /**
39361  * @class Roo.SplitLayoutRegion
39362  * @extends Roo.LayoutRegion
39363  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39364  */
39365 Roo.bootstrap.layout.Split = function(config){
39366     this.cursor = config.cursor;
39367     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39368 };
39369
39370 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39371 {
39372     splitTip : "Drag to resize.",
39373     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39374     useSplitTips : false,
39375
39376     applyConfig : function(config){
39377         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39378     },
39379     
39380     onRender : function(ctr,pos) {
39381         
39382         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39383         if(!this.config.split){
39384             return;
39385         }
39386         if(!this.split){
39387             
39388             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39389                             tag: "div",
39390                             id: this.el.id + "-split",
39391                             cls: "roo-layout-split roo-layout-split-"+this.position,
39392                             html: "&#160;"
39393             });
39394             /** The SplitBar for this region 
39395             * @type Roo.SplitBar */
39396             // does not exist yet...
39397             Roo.log([this.position, this.orientation]);
39398             
39399             this.split = new Roo.bootstrap.SplitBar({
39400                 dragElement : splitEl,
39401                 resizingElement: this.el,
39402                 orientation : this.orientation
39403             });
39404             
39405             this.split.on("moved", this.onSplitMove, this);
39406             this.split.useShim = this.config.useShim === true;
39407             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39408             if(this.useSplitTips){
39409                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39410             }
39411             //if(config.collapsible){
39412             //    this.split.el.on("dblclick", this.collapse,  this);
39413             //}
39414         }
39415         if(typeof this.config.minSize != "undefined"){
39416             this.split.minSize = this.config.minSize;
39417         }
39418         if(typeof this.config.maxSize != "undefined"){
39419             this.split.maxSize = this.config.maxSize;
39420         }
39421         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39422             this.hideSplitter();
39423         }
39424         
39425     },
39426
39427     getHMaxSize : function(){
39428          var cmax = this.config.maxSize || 10000;
39429          var center = this.mgr.getRegion("center");
39430          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39431     },
39432
39433     getVMaxSize : function(){
39434          var cmax = this.config.maxSize || 10000;
39435          var center = this.mgr.getRegion("center");
39436          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39437     },
39438
39439     onSplitMove : function(split, newSize){
39440         this.fireEvent("resized", this, newSize);
39441     },
39442     
39443     /** 
39444      * Returns the {@link Roo.SplitBar} for this region.
39445      * @return {Roo.SplitBar}
39446      */
39447     getSplitBar : function(){
39448         return this.split;
39449     },
39450     
39451     hide : function(){
39452         this.hideSplitter();
39453         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39454     },
39455
39456     hideSplitter : function(){
39457         if(this.split){
39458             this.split.el.setLocation(-2000,-2000);
39459             this.split.el.hide();
39460         }
39461     },
39462
39463     show : function(){
39464         if(this.split){
39465             this.split.el.show();
39466         }
39467         Roo.bootstrap.layout.Split.superclass.show.call(this);
39468     },
39469     
39470     beforeSlide: function(){
39471         if(Roo.isGecko){// firefox overflow auto bug workaround
39472             this.bodyEl.clip();
39473             if(this.tabs) {
39474                 this.tabs.bodyEl.clip();
39475             }
39476             if(this.activePanel){
39477                 this.activePanel.getEl().clip();
39478                 
39479                 if(this.activePanel.beforeSlide){
39480                     this.activePanel.beforeSlide();
39481                 }
39482             }
39483         }
39484     },
39485     
39486     afterSlide : function(){
39487         if(Roo.isGecko){// firefox overflow auto bug workaround
39488             this.bodyEl.unclip();
39489             if(this.tabs) {
39490                 this.tabs.bodyEl.unclip();
39491             }
39492             if(this.activePanel){
39493                 this.activePanel.getEl().unclip();
39494                 if(this.activePanel.afterSlide){
39495                     this.activePanel.afterSlide();
39496                 }
39497             }
39498         }
39499     },
39500
39501     initAutoHide : function(){
39502         if(this.autoHide !== false){
39503             if(!this.autoHideHd){
39504                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39505                 this.autoHideHd = {
39506                     "mouseout": function(e){
39507                         if(!e.within(this.el, true)){
39508                             st.delay(500);
39509                         }
39510                     },
39511                     "mouseover" : function(e){
39512                         st.cancel();
39513                     },
39514                     scope : this
39515                 };
39516             }
39517             this.el.on(this.autoHideHd);
39518         }
39519     },
39520
39521     clearAutoHide : function(){
39522         if(this.autoHide !== false){
39523             this.el.un("mouseout", this.autoHideHd.mouseout);
39524             this.el.un("mouseover", this.autoHideHd.mouseover);
39525         }
39526     },
39527
39528     clearMonitor : function(){
39529         Roo.get(document).un("click", this.slideInIf, this);
39530     },
39531
39532     // these names are backwards but not changed for compat
39533     slideOut : function(){
39534         if(this.isSlid || this.el.hasActiveFx()){
39535             return;
39536         }
39537         this.isSlid = true;
39538         if(this.collapseBtn){
39539             this.collapseBtn.hide();
39540         }
39541         this.closeBtnState = this.closeBtn.getStyle('display');
39542         this.closeBtn.hide();
39543         if(this.stickBtn){
39544             this.stickBtn.show();
39545         }
39546         this.el.show();
39547         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39548         this.beforeSlide();
39549         this.el.setStyle("z-index", 10001);
39550         this.el.slideIn(this.getSlideAnchor(), {
39551             callback: function(){
39552                 this.afterSlide();
39553                 this.initAutoHide();
39554                 Roo.get(document).on("click", this.slideInIf, this);
39555                 this.fireEvent("slideshow", this);
39556             },
39557             scope: this,
39558             block: true
39559         });
39560     },
39561
39562     afterSlideIn : function(){
39563         this.clearAutoHide();
39564         this.isSlid = false;
39565         this.clearMonitor();
39566         this.el.setStyle("z-index", "");
39567         if(this.collapseBtn){
39568             this.collapseBtn.show();
39569         }
39570         this.closeBtn.setStyle('display', this.closeBtnState);
39571         if(this.stickBtn){
39572             this.stickBtn.hide();
39573         }
39574         this.fireEvent("slidehide", this);
39575     },
39576
39577     slideIn : function(cb){
39578         if(!this.isSlid || this.el.hasActiveFx()){
39579             Roo.callback(cb);
39580             return;
39581         }
39582         this.isSlid = false;
39583         this.beforeSlide();
39584         this.el.slideOut(this.getSlideAnchor(), {
39585             callback: function(){
39586                 this.el.setLeftTop(-10000, -10000);
39587                 this.afterSlide();
39588                 this.afterSlideIn();
39589                 Roo.callback(cb);
39590             },
39591             scope: this,
39592             block: true
39593         });
39594     },
39595     
39596     slideInIf : function(e){
39597         if(!e.within(this.el)){
39598             this.slideIn();
39599         }
39600     },
39601
39602     animateCollapse : function(){
39603         this.beforeSlide();
39604         this.el.setStyle("z-index", 20000);
39605         var anchor = this.getSlideAnchor();
39606         this.el.slideOut(anchor, {
39607             callback : function(){
39608                 this.el.setStyle("z-index", "");
39609                 this.collapsedEl.slideIn(anchor, {duration:.3});
39610                 this.afterSlide();
39611                 this.el.setLocation(-10000,-10000);
39612                 this.el.hide();
39613                 this.fireEvent("collapsed", this);
39614             },
39615             scope: this,
39616             block: true
39617         });
39618     },
39619
39620     animateExpand : function(){
39621         this.beforeSlide();
39622         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39623         this.el.setStyle("z-index", 20000);
39624         this.collapsedEl.hide({
39625             duration:.1
39626         });
39627         this.el.slideIn(this.getSlideAnchor(), {
39628             callback : function(){
39629                 this.el.setStyle("z-index", "");
39630                 this.afterSlide();
39631                 if(this.split){
39632                     this.split.el.show();
39633                 }
39634                 this.fireEvent("invalidated", this);
39635                 this.fireEvent("expanded", this);
39636             },
39637             scope: this,
39638             block: true
39639         });
39640     },
39641
39642     anchors : {
39643         "west" : "left",
39644         "east" : "right",
39645         "north" : "top",
39646         "south" : "bottom"
39647     },
39648
39649     sanchors : {
39650         "west" : "l",
39651         "east" : "r",
39652         "north" : "t",
39653         "south" : "b"
39654     },
39655
39656     canchors : {
39657         "west" : "tl-tr",
39658         "east" : "tr-tl",
39659         "north" : "tl-bl",
39660         "south" : "bl-tl"
39661     },
39662
39663     getAnchor : function(){
39664         return this.anchors[this.position];
39665     },
39666
39667     getCollapseAnchor : function(){
39668         return this.canchors[this.position];
39669     },
39670
39671     getSlideAnchor : function(){
39672         return this.sanchors[this.position];
39673     },
39674
39675     getAlignAdj : function(){
39676         var cm = this.cmargins;
39677         switch(this.position){
39678             case "west":
39679                 return [0, 0];
39680             break;
39681             case "east":
39682                 return [0, 0];
39683             break;
39684             case "north":
39685                 return [0, 0];
39686             break;
39687             case "south":
39688                 return [0, 0];
39689             break;
39690         }
39691     },
39692
39693     getExpandAdj : function(){
39694         var c = this.collapsedEl, cm = this.cmargins;
39695         switch(this.position){
39696             case "west":
39697                 return [-(cm.right+c.getWidth()+cm.left), 0];
39698             break;
39699             case "east":
39700                 return [cm.right+c.getWidth()+cm.left, 0];
39701             break;
39702             case "north":
39703                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39704             break;
39705             case "south":
39706                 return [0, cm.top+cm.bottom+c.getHeight()];
39707             break;
39708         }
39709     }
39710 });/*
39711  * Based on:
39712  * Ext JS Library 1.1.1
39713  * Copyright(c) 2006-2007, Ext JS, LLC.
39714  *
39715  * Originally Released Under LGPL - original licence link has changed is not relivant.
39716  *
39717  * Fork - LGPL
39718  * <script type="text/javascript">
39719  */
39720 /*
39721  * These classes are private internal classes
39722  */
39723 Roo.bootstrap.layout.Center = function(config){
39724     config.region = "center";
39725     Roo.bootstrap.layout.Region.call(this, config);
39726     this.visible = true;
39727     this.minWidth = config.minWidth || 20;
39728     this.minHeight = config.minHeight || 20;
39729 };
39730
39731 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39732     hide : function(){
39733         // center panel can't be hidden
39734     },
39735     
39736     show : function(){
39737         // center panel can't be hidden
39738     },
39739     
39740     getMinWidth: function(){
39741         return this.minWidth;
39742     },
39743     
39744     getMinHeight: function(){
39745         return this.minHeight;
39746     }
39747 });
39748
39749
39750
39751
39752  
39753
39754
39755
39756
39757
39758
39759 Roo.bootstrap.layout.North = function(config)
39760 {
39761     config.region = 'north';
39762     config.cursor = 'n-resize';
39763     
39764     Roo.bootstrap.layout.Split.call(this, config);
39765     
39766     
39767     if(this.split){
39768         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39769         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39770         this.split.el.addClass("roo-layout-split-v");
39771     }
39772     //var size = config.initialSize || config.height;
39773     //if(this.el && typeof size != "undefined"){
39774     //    this.el.setHeight(size);
39775     //}
39776 };
39777 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39778 {
39779     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39780      
39781      
39782     onRender : function(ctr, pos)
39783     {
39784         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39785         var size = this.config.initialSize || this.config.height;
39786         if(this.el && typeof size != "undefined"){
39787             this.el.setHeight(size);
39788         }
39789     
39790     },
39791     
39792     getBox : function(){
39793         if(this.collapsed){
39794             return this.collapsedEl.getBox();
39795         }
39796         var box = this.el.getBox();
39797         if(this.split){
39798             box.height += this.split.el.getHeight();
39799         }
39800         return box;
39801     },
39802     
39803     updateBox : function(box){
39804         if(this.split && !this.collapsed){
39805             box.height -= this.split.el.getHeight();
39806             this.split.el.setLeft(box.x);
39807             this.split.el.setTop(box.y+box.height);
39808             this.split.el.setWidth(box.width);
39809         }
39810         if(this.collapsed){
39811             this.updateBody(box.width, null);
39812         }
39813         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39814     }
39815 });
39816
39817
39818
39819
39820
39821 Roo.bootstrap.layout.South = function(config){
39822     config.region = 'south';
39823     config.cursor = 's-resize';
39824     Roo.bootstrap.layout.Split.call(this, config);
39825     if(this.split){
39826         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39827         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39828         this.split.el.addClass("roo-layout-split-v");
39829     }
39830     
39831 };
39832
39833 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39834     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39835     
39836     onRender : function(ctr, pos)
39837     {
39838         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39839         var size = this.config.initialSize || this.config.height;
39840         if(this.el && typeof size != "undefined"){
39841             this.el.setHeight(size);
39842         }
39843     
39844     },
39845     
39846     getBox : function(){
39847         if(this.collapsed){
39848             return this.collapsedEl.getBox();
39849         }
39850         var box = this.el.getBox();
39851         if(this.split){
39852             var sh = this.split.el.getHeight();
39853             box.height += sh;
39854             box.y -= sh;
39855         }
39856         return box;
39857     },
39858     
39859     updateBox : function(box){
39860         if(this.split && !this.collapsed){
39861             var sh = this.split.el.getHeight();
39862             box.height -= sh;
39863             box.y += sh;
39864             this.split.el.setLeft(box.x);
39865             this.split.el.setTop(box.y-sh);
39866             this.split.el.setWidth(box.width);
39867         }
39868         if(this.collapsed){
39869             this.updateBody(box.width, null);
39870         }
39871         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39872     }
39873 });
39874
39875 Roo.bootstrap.layout.East = function(config){
39876     config.region = "east";
39877     config.cursor = "e-resize";
39878     Roo.bootstrap.layout.Split.call(this, config);
39879     if(this.split){
39880         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39881         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39882         this.split.el.addClass("roo-layout-split-h");
39883     }
39884     
39885 };
39886 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39887     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39888     
39889     onRender : function(ctr, pos)
39890     {
39891         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39892         var size = this.config.initialSize || this.config.width;
39893         if(this.el && typeof size != "undefined"){
39894             this.el.setWidth(size);
39895         }
39896     
39897     },
39898     
39899     getBox : function(){
39900         if(this.collapsed){
39901             return this.collapsedEl.getBox();
39902         }
39903         var box = this.el.getBox();
39904         if(this.split){
39905             var sw = this.split.el.getWidth();
39906             box.width += sw;
39907             box.x -= sw;
39908         }
39909         return box;
39910     },
39911
39912     updateBox : function(box){
39913         if(this.split && !this.collapsed){
39914             var sw = this.split.el.getWidth();
39915             box.width -= sw;
39916             this.split.el.setLeft(box.x);
39917             this.split.el.setTop(box.y);
39918             this.split.el.setHeight(box.height);
39919             box.x += sw;
39920         }
39921         if(this.collapsed){
39922             this.updateBody(null, box.height);
39923         }
39924         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39925     }
39926 });
39927
39928 Roo.bootstrap.layout.West = function(config){
39929     config.region = "west";
39930     config.cursor = "w-resize";
39931     
39932     Roo.bootstrap.layout.Split.call(this, config);
39933     if(this.split){
39934         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39935         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39936         this.split.el.addClass("roo-layout-split-h");
39937     }
39938     
39939 };
39940 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39941     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39942     
39943     onRender: function(ctr, pos)
39944     {
39945         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39946         var size = this.config.initialSize || this.config.width;
39947         if(typeof size != "undefined"){
39948             this.el.setWidth(size);
39949         }
39950     },
39951     
39952     getBox : function(){
39953         if(this.collapsed){
39954             return this.collapsedEl.getBox();
39955         }
39956         var box = this.el.getBox();
39957         if (box.width == 0) {
39958             box.width = this.config.width; // kludge?
39959         }
39960         if(this.split){
39961             box.width += this.split.el.getWidth();
39962         }
39963         return box;
39964     },
39965     
39966     updateBox : function(box){
39967         if(this.split && !this.collapsed){
39968             var sw = this.split.el.getWidth();
39969             box.width -= sw;
39970             this.split.el.setLeft(box.x+box.width);
39971             this.split.el.setTop(box.y);
39972             this.split.el.setHeight(box.height);
39973         }
39974         if(this.collapsed){
39975             this.updateBody(null, box.height);
39976         }
39977         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39978     }
39979 });Roo.namespace("Roo.bootstrap.panel");/*
39980  * Based on:
39981  * Ext JS Library 1.1.1
39982  * Copyright(c) 2006-2007, Ext JS, LLC.
39983  *
39984  * Originally Released Under LGPL - original licence link has changed is not relivant.
39985  *
39986  * Fork - LGPL
39987  * <script type="text/javascript">
39988  */
39989 /**
39990  * @class Roo.ContentPanel
39991  * @extends Roo.util.Observable
39992  * A basic ContentPanel element.
39993  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39994  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39995  * @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
39996  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39997  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39998  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39999  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40000  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40001  * @cfg {String} title          The title for this panel
40002  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40003  * @cfg {String} url            Calls {@link #setUrl} with this value
40004  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40005  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40006  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40007  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40008  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40009  * @cfg {Boolean} badges render the badges
40010  * @cfg {String} cls  extra classes to use  
40011  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40012
40013  * @constructor
40014  * Create a new ContentPanel.
40015  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40016  * @param {String/Object} config A string to set only the title or a config object
40017  * @param {String} content (optional) Set the HTML content for this panel
40018  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40019  */
40020 Roo.bootstrap.panel.Content = function( config){
40021     
40022     this.tpl = config.tpl || false;
40023     
40024     var el = config.el;
40025     var content = config.content;
40026
40027     if(config.autoCreate){ // xtype is available if this is called from factory
40028         el = Roo.id();
40029     }
40030     this.el = Roo.get(el);
40031     if(!this.el && config && config.autoCreate){
40032         if(typeof config.autoCreate == "object"){
40033             if(!config.autoCreate.id){
40034                 config.autoCreate.id = config.id||el;
40035             }
40036             this.el = Roo.DomHelper.append(document.body,
40037                         config.autoCreate, true);
40038         }else{
40039             var elcfg =  {
40040                 tag: "div",
40041                 cls: (config.cls || '') +
40042                     (config.background ? ' bg-' + config.background : '') +
40043                     " roo-layout-inactive-content",
40044                 id: config.id||el
40045             };
40046             if (config.iframe) {
40047                 elcfg.cn = [
40048                     {
40049                         tag : 'iframe',
40050                         style : 'border: 0px',
40051                         src : 'about:blank'
40052                     }
40053                 ];
40054             }
40055               
40056             if (config.html) {
40057                 elcfg.html = config.html;
40058                 
40059             }
40060                         
40061             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40062             if (config.iframe) {
40063                 this.iframeEl = this.el.select('iframe',true).first();
40064             }
40065             
40066         }
40067     } 
40068     this.closable = false;
40069     this.loaded = false;
40070     this.active = false;
40071    
40072       
40073     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40074         
40075         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40076         
40077         this.wrapEl = this.el; //this.el.wrap();
40078         var ti = [];
40079         if (config.toolbar.items) {
40080             ti = config.toolbar.items ;
40081             delete config.toolbar.items ;
40082         }
40083         
40084         var nitems = [];
40085         this.toolbar.render(this.wrapEl, 'before');
40086         for(var i =0;i < ti.length;i++) {
40087           //  Roo.log(['add child', items[i]]);
40088             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40089         }
40090         this.toolbar.items = nitems;
40091         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40092         delete config.toolbar;
40093         
40094     }
40095     /*
40096     // xtype created footer. - not sure if will work as we normally have to render first..
40097     if (this.footer && !this.footer.el && this.footer.xtype) {
40098         if (!this.wrapEl) {
40099             this.wrapEl = this.el.wrap();
40100         }
40101     
40102         this.footer.container = this.wrapEl.createChild();
40103          
40104         this.footer = Roo.factory(this.footer, Roo);
40105         
40106     }
40107     */
40108     
40109      if(typeof config == "string"){
40110         this.title = config;
40111     }else{
40112         Roo.apply(this, config);
40113     }
40114     
40115     if(this.resizeEl){
40116         this.resizeEl = Roo.get(this.resizeEl, true);
40117     }else{
40118         this.resizeEl = this.el;
40119     }
40120     // handle view.xtype
40121     
40122  
40123     
40124     
40125     this.addEvents({
40126         /**
40127          * @event activate
40128          * Fires when this panel is activated. 
40129          * @param {Roo.ContentPanel} this
40130          */
40131         "activate" : true,
40132         /**
40133          * @event deactivate
40134          * Fires when this panel is activated. 
40135          * @param {Roo.ContentPanel} this
40136          */
40137         "deactivate" : true,
40138
40139         /**
40140          * @event resize
40141          * Fires when this panel is resized if fitToFrame is true.
40142          * @param {Roo.ContentPanel} this
40143          * @param {Number} width The width after any component adjustments
40144          * @param {Number} height The height after any component adjustments
40145          */
40146         "resize" : true,
40147         
40148          /**
40149          * @event render
40150          * Fires when this tab is created
40151          * @param {Roo.ContentPanel} this
40152          */
40153         "render" : true
40154         
40155         
40156         
40157     });
40158     
40159
40160     
40161     
40162     if(this.autoScroll && !this.iframe){
40163         this.resizeEl.setStyle("overflow", "auto");
40164     } else {
40165         // fix randome scrolling
40166         //this.el.on('scroll', function() {
40167         //    Roo.log('fix random scolling');
40168         //    this.scrollTo('top',0); 
40169         //});
40170     }
40171     content = content || this.content;
40172     if(content){
40173         this.setContent(content);
40174     }
40175     if(config && config.url){
40176         this.setUrl(this.url, this.params, this.loadOnce);
40177     }
40178     
40179     
40180     
40181     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40182     
40183     if (this.view && typeof(this.view.xtype) != 'undefined') {
40184         this.view.el = this.el.appendChild(document.createElement("div"));
40185         this.view = Roo.factory(this.view); 
40186         this.view.render  &&  this.view.render(false, '');  
40187     }
40188     
40189     
40190     this.fireEvent('render', this);
40191 };
40192
40193 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40194     
40195     cls : '',
40196     background : '',
40197     
40198     tabTip : '',
40199     
40200     iframe : false,
40201     iframeEl : false,
40202     
40203     setRegion : function(region){
40204         this.region = region;
40205         this.setActiveClass(region && !this.background);
40206     },
40207     
40208     
40209     setActiveClass: function(state)
40210     {
40211         if(state){
40212            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40213            this.el.setStyle('position','relative');
40214         }else{
40215            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40216            this.el.setStyle('position', 'absolute');
40217         } 
40218     },
40219     
40220     /**
40221      * Returns the toolbar for this Panel if one was configured. 
40222      * @return {Roo.Toolbar} 
40223      */
40224     getToolbar : function(){
40225         return this.toolbar;
40226     },
40227     
40228     setActiveState : function(active)
40229     {
40230         this.active = active;
40231         this.setActiveClass(active);
40232         if(!active){
40233             if(this.fireEvent("deactivate", this) === false){
40234                 return false;
40235             }
40236             return true;
40237         }
40238         this.fireEvent("activate", this);
40239         return true;
40240     },
40241     /**
40242      * Updates this panel's element (not for iframe)
40243      * @param {String} content The new content
40244      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40245     */
40246     setContent : function(content, loadScripts){
40247         if (this.iframe) {
40248             return;
40249         }
40250         
40251         this.el.update(content, loadScripts);
40252     },
40253
40254     ignoreResize : function(w, h){
40255         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40256             return true;
40257         }else{
40258             this.lastSize = {width: w, height: h};
40259             return false;
40260         }
40261     },
40262     /**
40263      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40264      * @return {Roo.UpdateManager} The UpdateManager
40265      */
40266     getUpdateManager : function(){
40267         if (this.iframe) {
40268             return false;
40269         }
40270         return this.el.getUpdateManager();
40271     },
40272      /**
40273      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40274      * Does not work with IFRAME contents
40275      * @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:
40276 <pre><code>
40277 panel.load({
40278     url: "your-url.php",
40279     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40280     callback: yourFunction,
40281     scope: yourObject, //(optional scope)
40282     discardUrl: false,
40283     nocache: false,
40284     text: "Loading...",
40285     timeout: 30,
40286     scripts: false
40287 });
40288 </code></pre>
40289      
40290      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40291      * 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.
40292      * @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}
40293      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40294      * @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.
40295      * @return {Roo.ContentPanel} this
40296      */
40297     load : function(){
40298         
40299         if (this.iframe) {
40300             return this;
40301         }
40302         
40303         var um = this.el.getUpdateManager();
40304         um.update.apply(um, arguments);
40305         return this;
40306     },
40307
40308
40309     /**
40310      * 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.
40311      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40312      * @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)
40313      * @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)
40314      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40315      */
40316     setUrl : function(url, params, loadOnce){
40317         if (this.iframe) {
40318             this.iframeEl.dom.src = url;
40319             return false;
40320         }
40321         
40322         if(this.refreshDelegate){
40323             this.removeListener("activate", this.refreshDelegate);
40324         }
40325         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40326         this.on("activate", this.refreshDelegate);
40327         return this.el.getUpdateManager();
40328     },
40329     
40330     _handleRefresh : function(url, params, loadOnce){
40331         if(!loadOnce || !this.loaded){
40332             var updater = this.el.getUpdateManager();
40333             updater.update(url, params, this._setLoaded.createDelegate(this));
40334         }
40335     },
40336     
40337     _setLoaded : function(){
40338         this.loaded = true;
40339     }, 
40340     
40341     /**
40342      * Returns this panel's id
40343      * @return {String} 
40344      */
40345     getId : function(){
40346         return this.el.id;
40347     },
40348     
40349     /** 
40350      * Returns this panel's element - used by regiosn to add.
40351      * @return {Roo.Element} 
40352      */
40353     getEl : function(){
40354         return this.wrapEl || this.el;
40355     },
40356     
40357    
40358     
40359     adjustForComponents : function(width, height)
40360     {
40361         //Roo.log('adjustForComponents ');
40362         if(this.resizeEl != this.el){
40363             width -= this.el.getFrameWidth('lr');
40364             height -= this.el.getFrameWidth('tb');
40365         }
40366         if(this.toolbar){
40367             var te = this.toolbar.getEl();
40368             te.setWidth(width);
40369             height -= te.getHeight();
40370         }
40371         if(this.footer){
40372             var te = this.footer.getEl();
40373             te.setWidth(width);
40374             height -= te.getHeight();
40375         }
40376         
40377         
40378         if(this.adjustments){
40379             width += this.adjustments[0];
40380             height += this.adjustments[1];
40381         }
40382         return {"width": width, "height": height};
40383     },
40384     
40385     setSize : function(width, height){
40386         if(this.fitToFrame && !this.ignoreResize(width, height)){
40387             if(this.fitContainer && this.resizeEl != this.el){
40388                 this.el.setSize(width, height);
40389             }
40390             var size = this.adjustForComponents(width, height);
40391             if (this.iframe) {
40392                 this.iframeEl.setSize(width,height);
40393             }
40394             
40395             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40396             this.fireEvent('resize', this, size.width, size.height);
40397             
40398             
40399         }
40400     },
40401     
40402     /**
40403      * Returns this panel's title
40404      * @return {String} 
40405      */
40406     getTitle : function(){
40407         
40408         if (typeof(this.title) != 'object') {
40409             return this.title;
40410         }
40411         
40412         var t = '';
40413         for (var k in this.title) {
40414             if (!this.title.hasOwnProperty(k)) {
40415                 continue;
40416             }
40417             
40418             if (k.indexOf('-') >= 0) {
40419                 var s = k.split('-');
40420                 for (var i = 0; i<s.length; i++) {
40421                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40422                 }
40423             } else {
40424                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40425             }
40426         }
40427         return t;
40428     },
40429     
40430     /**
40431      * Set this panel's title
40432      * @param {String} title
40433      */
40434     setTitle : function(title){
40435         this.title = title;
40436         if(this.region){
40437             this.region.updatePanelTitle(this, title);
40438         }
40439     },
40440     
40441     /**
40442      * Returns true is this panel was configured to be closable
40443      * @return {Boolean} 
40444      */
40445     isClosable : function(){
40446         return this.closable;
40447     },
40448     
40449     beforeSlide : function(){
40450         this.el.clip();
40451         this.resizeEl.clip();
40452     },
40453     
40454     afterSlide : function(){
40455         this.el.unclip();
40456         this.resizeEl.unclip();
40457     },
40458     
40459     /**
40460      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40461      *   Will fail silently if the {@link #setUrl} method has not been called.
40462      *   This does not activate the panel, just updates its content.
40463      */
40464     refresh : function(){
40465         if(this.refreshDelegate){
40466            this.loaded = false;
40467            this.refreshDelegate();
40468         }
40469     },
40470     
40471     /**
40472      * Destroys this panel
40473      */
40474     destroy : function(){
40475         this.el.removeAllListeners();
40476         var tempEl = document.createElement("span");
40477         tempEl.appendChild(this.el.dom);
40478         tempEl.innerHTML = "";
40479         this.el.remove();
40480         this.el = null;
40481     },
40482     
40483     /**
40484      * form - if the content panel contains a form - this is a reference to it.
40485      * @type {Roo.form.Form}
40486      */
40487     form : false,
40488     /**
40489      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40490      *    This contains a reference to it.
40491      * @type {Roo.View}
40492      */
40493     view : false,
40494     
40495       /**
40496      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40497      * <pre><code>
40498
40499 layout.addxtype({
40500        xtype : 'Form',
40501        items: [ .... ]
40502    }
40503 );
40504
40505 </code></pre>
40506      * @param {Object} cfg Xtype definition of item to add.
40507      */
40508     
40509     
40510     getChildContainer: function () {
40511         return this.getEl();
40512     }
40513     
40514     
40515     /*
40516         var  ret = new Roo.factory(cfg);
40517         return ret;
40518         
40519         
40520         // add form..
40521         if (cfg.xtype.match(/^Form$/)) {
40522             
40523             var el;
40524             //if (this.footer) {
40525             //    el = this.footer.container.insertSibling(false, 'before');
40526             //} else {
40527                 el = this.el.createChild();
40528             //}
40529
40530             this.form = new  Roo.form.Form(cfg);
40531             
40532             
40533             if ( this.form.allItems.length) {
40534                 this.form.render(el.dom);
40535             }
40536             return this.form;
40537         }
40538         // should only have one of theses..
40539         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40540             // views.. should not be just added - used named prop 'view''
40541             
40542             cfg.el = this.el.appendChild(document.createElement("div"));
40543             // factory?
40544             
40545             var ret = new Roo.factory(cfg);
40546              
40547              ret.render && ret.render(false, ''); // render blank..
40548             this.view = ret;
40549             return ret;
40550         }
40551         return false;
40552     }
40553     \*/
40554 });
40555  
40556 /**
40557  * @class Roo.bootstrap.panel.Grid
40558  * @extends Roo.bootstrap.panel.Content
40559  * @constructor
40560  * Create a new GridPanel.
40561  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40562  * @param {Object} config A the config object
40563   
40564  */
40565
40566
40567
40568 Roo.bootstrap.panel.Grid = function(config)
40569 {
40570     
40571       
40572     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40573         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40574
40575     config.el = this.wrapper;
40576     //this.el = this.wrapper;
40577     
40578       if (config.container) {
40579         // ctor'ed from a Border/panel.grid
40580         
40581         
40582         this.wrapper.setStyle("overflow", "hidden");
40583         this.wrapper.addClass('roo-grid-container');
40584
40585     }
40586     
40587     
40588     if(config.toolbar){
40589         var tool_el = this.wrapper.createChild();    
40590         this.toolbar = Roo.factory(config.toolbar);
40591         var ti = [];
40592         if (config.toolbar.items) {
40593             ti = config.toolbar.items ;
40594             delete config.toolbar.items ;
40595         }
40596         
40597         var nitems = [];
40598         this.toolbar.render(tool_el);
40599         for(var i =0;i < ti.length;i++) {
40600           //  Roo.log(['add child', items[i]]);
40601             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40602         }
40603         this.toolbar.items = nitems;
40604         
40605         delete config.toolbar;
40606     }
40607     
40608     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40609     config.grid.scrollBody = true;;
40610     config.grid.monitorWindowResize = false; // turn off autosizing
40611     config.grid.autoHeight = false;
40612     config.grid.autoWidth = false;
40613     
40614     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40615     
40616     if (config.background) {
40617         // render grid on panel activation (if panel background)
40618         this.on('activate', function(gp) {
40619             if (!gp.grid.rendered) {
40620                 gp.grid.render(this.wrapper);
40621                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40622             }
40623         });
40624             
40625     } else {
40626         this.grid.render(this.wrapper);
40627         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40628
40629     }
40630     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40631     // ??? needed ??? config.el = this.wrapper;
40632     
40633     
40634     
40635   
40636     // xtype created footer. - not sure if will work as we normally have to render first..
40637     if (this.footer && !this.footer.el && this.footer.xtype) {
40638         
40639         var ctr = this.grid.getView().getFooterPanel(true);
40640         this.footer.dataSource = this.grid.dataSource;
40641         this.footer = Roo.factory(this.footer, Roo);
40642         this.footer.render(ctr);
40643         
40644     }
40645     
40646     
40647     
40648     
40649      
40650 };
40651
40652 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40653     getId : function(){
40654         return this.grid.id;
40655     },
40656     
40657     /**
40658      * Returns the grid for this panel
40659      * @return {Roo.bootstrap.Table} 
40660      */
40661     getGrid : function(){
40662         return this.grid;    
40663     },
40664     
40665     setSize : function(width, height){
40666         if(!this.ignoreResize(width, height)){
40667             var grid = this.grid;
40668             var size = this.adjustForComponents(width, height);
40669             // tfoot is not a footer?
40670           
40671             
40672             var gridel = grid.getGridEl();
40673             gridel.setSize(size.width, size.height);
40674             
40675             var tbd = grid.getGridEl().select('tbody', true).first();
40676             var thd = grid.getGridEl().select('thead',true).first();
40677             var tbf= grid.getGridEl().select('tfoot', true).first();
40678
40679             if (tbf) {
40680                 size.height -= tbf.getHeight();
40681             }
40682             if (thd) {
40683                 size.height -= thd.getHeight();
40684             }
40685             
40686             tbd.setSize(size.width, size.height );
40687             // this is for the account management tab -seems to work there.
40688             var thd = grid.getGridEl().select('thead',true).first();
40689             //if (tbd) {
40690             //    tbd.setSize(size.width, size.height - thd.getHeight());
40691             //}
40692              
40693             grid.autoSize();
40694         }
40695     },
40696      
40697     
40698     
40699     beforeSlide : function(){
40700         this.grid.getView().scroller.clip();
40701     },
40702     
40703     afterSlide : function(){
40704         this.grid.getView().scroller.unclip();
40705     },
40706     
40707     destroy : function(){
40708         this.grid.destroy();
40709         delete this.grid;
40710         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40711     }
40712 });
40713
40714 /**
40715  * @class Roo.bootstrap.panel.Nest
40716  * @extends Roo.bootstrap.panel.Content
40717  * @constructor
40718  * Create a new Panel, that can contain a layout.Border.
40719  * 
40720  * 
40721  * @param {Roo.BorderLayout} layout The layout for this panel
40722  * @param {String/Object} config A string to set only the title or a config object
40723  */
40724 Roo.bootstrap.panel.Nest = function(config)
40725 {
40726     // construct with only one argument..
40727     /* FIXME - implement nicer consturctors
40728     if (layout.layout) {
40729         config = layout;
40730         layout = config.layout;
40731         delete config.layout;
40732     }
40733     if (layout.xtype && !layout.getEl) {
40734         // then layout needs constructing..
40735         layout = Roo.factory(layout, Roo);
40736     }
40737     */
40738     
40739     config.el =  config.layout.getEl();
40740     
40741     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40742     
40743     config.layout.monitorWindowResize = false; // turn off autosizing
40744     this.layout = config.layout;
40745     this.layout.getEl().addClass("roo-layout-nested-layout");
40746     this.layout.parent = this;
40747     
40748     
40749     
40750     
40751 };
40752
40753 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40754
40755     setSize : function(width, height){
40756         if(!this.ignoreResize(width, height)){
40757             var size = this.adjustForComponents(width, height);
40758             var el = this.layout.getEl();
40759             if (size.height < 1) {
40760                 el.setWidth(size.width);   
40761             } else {
40762                 el.setSize(size.width, size.height);
40763             }
40764             var touch = el.dom.offsetWidth;
40765             this.layout.layout();
40766             // ie requires a double layout on the first pass
40767             if(Roo.isIE && !this.initialized){
40768                 this.initialized = true;
40769                 this.layout.layout();
40770             }
40771         }
40772     },
40773     
40774     // activate all subpanels if not currently active..
40775     
40776     setActiveState : function(active){
40777         this.active = active;
40778         this.setActiveClass(active);
40779         
40780         if(!active){
40781             this.fireEvent("deactivate", this);
40782             return;
40783         }
40784         
40785         this.fireEvent("activate", this);
40786         // not sure if this should happen before or after..
40787         if (!this.layout) {
40788             return; // should not happen..
40789         }
40790         var reg = false;
40791         for (var r in this.layout.regions) {
40792             reg = this.layout.getRegion(r);
40793             if (reg.getActivePanel()) {
40794                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40795                 reg.setActivePanel(reg.getActivePanel());
40796                 continue;
40797             }
40798             if (!reg.panels.length) {
40799                 continue;
40800             }
40801             reg.showPanel(reg.getPanel(0));
40802         }
40803         
40804         
40805         
40806         
40807     },
40808     
40809     /**
40810      * Returns the nested BorderLayout for this panel
40811      * @return {Roo.BorderLayout} 
40812      */
40813     getLayout : function(){
40814         return this.layout;
40815     },
40816     
40817      /**
40818      * Adds a xtype elements to the layout of the nested panel
40819      * <pre><code>
40820
40821 panel.addxtype({
40822        xtype : 'ContentPanel',
40823        region: 'west',
40824        items: [ .... ]
40825    }
40826 );
40827
40828 panel.addxtype({
40829         xtype : 'NestedLayoutPanel',
40830         region: 'west',
40831         layout: {
40832            center: { },
40833            west: { }   
40834         },
40835         items : [ ... list of content panels or nested layout panels.. ]
40836    }
40837 );
40838 </code></pre>
40839      * @param {Object} cfg Xtype definition of item to add.
40840      */
40841     addxtype : function(cfg) {
40842         return this.layout.addxtype(cfg);
40843     
40844     }
40845 });/*
40846  * Based on:
40847  * Ext JS Library 1.1.1
40848  * Copyright(c) 2006-2007, Ext JS, LLC.
40849  *
40850  * Originally Released Under LGPL - original licence link has changed is not relivant.
40851  *
40852  * Fork - LGPL
40853  * <script type="text/javascript">
40854  */
40855 /**
40856  * @class Roo.TabPanel
40857  * @extends Roo.util.Observable
40858  * A lightweight tab container.
40859  * <br><br>
40860  * Usage:
40861  * <pre><code>
40862 // basic tabs 1, built from existing content
40863 var tabs = new Roo.TabPanel("tabs1");
40864 tabs.addTab("script", "View Script");
40865 tabs.addTab("markup", "View Markup");
40866 tabs.activate("script");
40867
40868 // more advanced tabs, built from javascript
40869 var jtabs = new Roo.TabPanel("jtabs");
40870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40871
40872 // set up the UpdateManager
40873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40874 var updater = tab2.getUpdateManager();
40875 updater.setDefaultUrl("ajax1.htm");
40876 tab2.on('activate', updater.refresh, updater, true);
40877
40878 // Use setUrl for Ajax loading
40879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40880 tab3.setUrl("ajax2.htm", null, true);
40881
40882 // Disabled tab
40883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40884 tab4.disable();
40885
40886 jtabs.activate("jtabs-1");
40887  * </code></pre>
40888  * @constructor
40889  * Create a new TabPanel.
40890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40892  */
40893 Roo.bootstrap.panel.Tabs = function(config){
40894     /**
40895     * The container element for this TabPanel.
40896     * @type Roo.Element
40897     */
40898     this.el = Roo.get(config.el);
40899     delete config.el;
40900     if(config){
40901         if(typeof config == "boolean"){
40902             this.tabPosition = config ? "bottom" : "top";
40903         }else{
40904             Roo.apply(this, config);
40905         }
40906     }
40907     
40908     if(this.tabPosition == "bottom"){
40909         // if tabs are at the bottom = create the body first.
40910         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40911         this.el.addClass("roo-tabs-bottom");
40912     }
40913     // next create the tabs holders
40914     
40915     if (this.tabPosition == "west"){
40916         
40917         var reg = this.region; // fake it..
40918         while (reg) {
40919             if (!reg.mgr.parent) {
40920                 break;
40921             }
40922             reg = reg.mgr.parent.region;
40923         }
40924         Roo.log("got nest?");
40925         Roo.log(reg);
40926         if (reg.mgr.getRegion('west')) {
40927             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40928             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40929             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40930             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40931             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40932         
40933             
40934         }
40935         
40936         
40937     } else {
40938      
40939         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40940         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40941         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40942         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40943     }
40944     
40945     
40946     if(Roo.isIE){
40947         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40948     }
40949     
40950     // finally - if tabs are at the top, then create the body last..
40951     if(this.tabPosition != "bottom"){
40952         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40953          * @type Roo.Element
40954          */
40955         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40956         this.el.addClass("roo-tabs-top");
40957     }
40958     this.items = [];
40959
40960     this.bodyEl.setStyle("position", "relative");
40961
40962     this.active = null;
40963     this.activateDelegate = this.activate.createDelegate(this);
40964
40965     this.addEvents({
40966         /**
40967          * @event tabchange
40968          * Fires when the active tab changes
40969          * @param {Roo.TabPanel} this
40970          * @param {Roo.TabPanelItem} activePanel The new active tab
40971          */
40972         "tabchange": true,
40973         /**
40974          * @event beforetabchange
40975          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40976          * @param {Roo.TabPanel} this
40977          * @param {Object} e Set cancel to true on this object to cancel the tab change
40978          * @param {Roo.TabPanelItem} tab The tab being changed to
40979          */
40980         "beforetabchange" : true
40981     });
40982
40983     Roo.EventManager.onWindowResize(this.onResize, this);
40984     this.cpad = this.el.getPadding("lr");
40985     this.hiddenCount = 0;
40986
40987
40988     // toolbar on the tabbar support...
40989     if (this.toolbar) {
40990         alert("no toolbar support yet");
40991         this.toolbar  = false;
40992         /*
40993         var tcfg = this.toolbar;
40994         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40995         this.toolbar = new Roo.Toolbar(tcfg);
40996         if (Roo.isSafari) {
40997             var tbl = tcfg.container.child('table', true);
40998             tbl.setAttribute('width', '100%');
40999         }
41000         */
41001         
41002     }
41003    
41004
41005
41006     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41007 };
41008
41009 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41010     /*
41011      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41012      */
41013     tabPosition : "top",
41014     /*
41015      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41016      */
41017     currentTabWidth : 0,
41018     /*
41019      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41020      */
41021     minTabWidth : 40,
41022     /*
41023      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41024      */
41025     maxTabWidth : 250,
41026     /*
41027      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41028      */
41029     preferredTabWidth : 175,
41030     /*
41031      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41032      */
41033     resizeTabs : false,
41034     /*
41035      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41036      */
41037     monitorResize : true,
41038     /*
41039      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41040      */
41041     toolbar : false,  // set by caller..
41042     
41043     region : false, /// set by caller
41044     
41045     disableTooltips : true, // not used yet...
41046
41047     /**
41048      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41049      * @param {String} id The id of the div to use <b>or create</b>
41050      * @param {String} text The text for the tab
41051      * @param {String} content (optional) Content to put in the TabPanelItem body
41052      * @param {Boolean} closable (optional) True to create a close icon on the tab
41053      * @return {Roo.TabPanelItem} The created TabPanelItem
41054      */
41055     addTab : function(id, text, content, closable, tpl)
41056     {
41057         var item = new Roo.bootstrap.panel.TabItem({
41058             panel: this,
41059             id : id,
41060             text : text,
41061             closable : closable,
41062             tpl : tpl
41063         });
41064         this.addTabItem(item);
41065         if(content){
41066             item.setContent(content);
41067         }
41068         return item;
41069     },
41070
41071     /**
41072      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41073      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41074      * @return {Roo.TabPanelItem}
41075      */
41076     getTab : function(id){
41077         return this.items[id];
41078     },
41079
41080     /**
41081      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41082      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41083      */
41084     hideTab : function(id){
41085         var t = this.items[id];
41086         if(!t.isHidden()){
41087            t.setHidden(true);
41088            this.hiddenCount++;
41089            this.autoSizeTabs();
41090         }
41091     },
41092
41093     /**
41094      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41095      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41096      */
41097     unhideTab : function(id){
41098         var t = this.items[id];
41099         if(t.isHidden()){
41100            t.setHidden(false);
41101            this.hiddenCount--;
41102            this.autoSizeTabs();
41103         }
41104     },
41105
41106     /**
41107      * Adds an existing {@link Roo.TabPanelItem}.
41108      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41109      */
41110     addTabItem : function(item)
41111     {
41112         this.items[item.id] = item;
41113         this.items.push(item);
41114         this.autoSizeTabs();
41115       //  if(this.resizeTabs){
41116     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41117   //         this.autoSizeTabs();
41118 //        }else{
41119 //            item.autoSize();
41120        // }
41121     },
41122
41123     /**
41124      * Removes a {@link Roo.TabPanelItem}.
41125      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41126      */
41127     removeTab : function(id){
41128         var items = this.items;
41129         var tab = items[id];
41130         if(!tab) { return; }
41131         var index = items.indexOf(tab);
41132         if(this.active == tab && items.length > 1){
41133             var newTab = this.getNextAvailable(index);
41134             if(newTab) {
41135                 newTab.activate();
41136             }
41137         }
41138         this.stripEl.dom.removeChild(tab.pnode.dom);
41139         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41140             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41141         }
41142         items.splice(index, 1);
41143         delete this.items[tab.id];
41144         tab.fireEvent("close", tab);
41145         tab.purgeListeners();
41146         this.autoSizeTabs();
41147     },
41148
41149     getNextAvailable : function(start){
41150         var items = this.items;
41151         var index = start;
41152         // look for a next tab that will slide over to
41153         // replace the one being removed
41154         while(index < items.length){
41155             var item = items[++index];
41156             if(item && !item.isHidden()){
41157                 return item;
41158             }
41159         }
41160         // if one isn't found select the previous tab (on the left)
41161         index = start;
41162         while(index >= 0){
41163             var item = items[--index];
41164             if(item && !item.isHidden()){
41165                 return item;
41166             }
41167         }
41168         return null;
41169     },
41170
41171     /**
41172      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41173      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41174      */
41175     disableTab : function(id){
41176         var tab = this.items[id];
41177         if(tab && this.active != tab){
41178             tab.disable();
41179         }
41180     },
41181
41182     /**
41183      * Enables a {@link Roo.TabPanelItem} that is disabled.
41184      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41185      */
41186     enableTab : function(id){
41187         var tab = this.items[id];
41188         tab.enable();
41189     },
41190
41191     /**
41192      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41193      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41194      * @return {Roo.TabPanelItem} The TabPanelItem.
41195      */
41196     activate : function(id)
41197     {
41198         //Roo.log('activite:'  + id);
41199         
41200         var tab = this.items[id];
41201         if(!tab){
41202             return null;
41203         }
41204         if(tab == this.active || tab.disabled){
41205             return tab;
41206         }
41207         var e = {};
41208         this.fireEvent("beforetabchange", this, e, tab);
41209         if(e.cancel !== true && !tab.disabled){
41210             if(this.active){
41211                 this.active.hide();
41212             }
41213             this.active = this.items[id];
41214             this.active.show();
41215             this.fireEvent("tabchange", this, this.active);
41216         }
41217         return tab;
41218     },
41219
41220     /**
41221      * Gets the active {@link Roo.TabPanelItem}.
41222      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41223      */
41224     getActiveTab : function(){
41225         return this.active;
41226     },
41227
41228     /**
41229      * Updates the tab body element to fit the height of the container element
41230      * for overflow scrolling
41231      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41232      */
41233     syncHeight : function(targetHeight){
41234         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41235         var bm = this.bodyEl.getMargins();
41236         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41237         this.bodyEl.setHeight(newHeight);
41238         return newHeight;
41239     },
41240
41241     onResize : function(){
41242         if(this.monitorResize){
41243             this.autoSizeTabs();
41244         }
41245     },
41246
41247     /**
41248      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41249      */
41250     beginUpdate : function(){
41251         this.updating = true;
41252     },
41253
41254     /**
41255      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41256      */
41257     endUpdate : function(){
41258         this.updating = false;
41259         this.autoSizeTabs();
41260     },
41261
41262     /**
41263      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41264      */
41265     autoSizeTabs : function()
41266     {
41267         var count = this.items.length;
41268         var vcount = count - this.hiddenCount;
41269         
41270         if (vcount < 2) {
41271             this.stripEl.hide();
41272         } else {
41273             this.stripEl.show();
41274         }
41275         
41276         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41277             return;
41278         }
41279         
41280         
41281         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41282         var availWidth = Math.floor(w / vcount);
41283         var b = this.stripBody;
41284         if(b.getWidth() > w){
41285             var tabs = this.items;
41286             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41287             if(availWidth < this.minTabWidth){
41288                 /*if(!this.sleft){    // incomplete scrolling code
41289                     this.createScrollButtons();
41290                 }
41291                 this.showScroll();
41292                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41293             }
41294         }else{
41295             if(this.currentTabWidth < this.preferredTabWidth){
41296                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41297             }
41298         }
41299     },
41300
41301     /**
41302      * Returns the number of tabs in this TabPanel.
41303      * @return {Number}
41304      */
41305      getCount : function(){
41306          return this.items.length;
41307      },
41308
41309     /**
41310      * Resizes all the tabs to the passed width
41311      * @param {Number} The new width
41312      */
41313     setTabWidth : function(width){
41314         this.currentTabWidth = width;
41315         for(var i = 0, len = this.items.length; i < len; i++) {
41316                 if(!this.items[i].isHidden()) {
41317                 this.items[i].setWidth(width);
41318             }
41319         }
41320     },
41321
41322     /**
41323      * Destroys this TabPanel
41324      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41325      */
41326     destroy : function(removeEl){
41327         Roo.EventManager.removeResizeListener(this.onResize, this);
41328         for(var i = 0, len = this.items.length; i < len; i++){
41329             this.items[i].purgeListeners();
41330         }
41331         if(removeEl === true){
41332             this.el.update("");
41333             this.el.remove();
41334         }
41335     },
41336     
41337     createStrip : function(container)
41338     {
41339         var strip = document.createElement("nav");
41340         strip.className = Roo.bootstrap.version == 4 ?
41341             "navbar-light bg-light" : 
41342             "navbar navbar-default"; //"x-tabs-wrap";
41343         container.appendChild(strip);
41344         return strip;
41345     },
41346     
41347     createStripList : function(strip)
41348     {
41349         // div wrapper for retard IE
41350         // returns the "tr" element.
41351         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41352         //'<div class="x-tabs-strip-wrap">'+
41353           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41354           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41355         return strip.firstChild; //.firstChild.firstChild.firstChild;
41356     },
41357     createBody : function(container)
41358     {
41359         var body = document.createElement("div");
41360         Roo.id(body, "tab-body");
41361         //Roo.fly(body).addClass("x-tabs-body");
41362         Roo.fly(body).addClass("tab-content");
41363         container.appendChild(body);
41364         return body;
41365     },
41366     createItemBody :function(bodyEl, id){
41367         var body = Roo.getDom(id);
41368         if(!body){
41369             body = document.createElement("div");
41370             body.id = id;
41371         }
41372         //Roo.fly(body).addClass("x-tabs-item-body");
41373         Roo.fly(body).addClass("tab-pane");
41374          bodyEl.insertBefore(body, bodyEl.firstChild);
41375         return body;
41376     },
41377     /** @private */
41378     createStripElements :  function(stripEl, text, closable, tpl)
41379     {
41380         var td = document.createElement("li"); // was td..
41381         td.className = 'nav-item';
41382         
41383         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41384         
41385         
41386         stripEl.appendChild(td);
41387         /*if(closable){
41388             td.className = "x-tabs-closable";
41389             if(!this.closeTpl){
41390                 this.closeTpl = new Roo.Template(
41391                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41392                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41393                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41394                 );
41395             }
41396             var el = this.closeTpl.overwrite(td, {"text": text});
41397             var close = el.getElementsByTagName("div")[0];
41398             var inner = el.getElementsByTagName("em")[0];
41399             return {"el": el, "close": close, "inner": inner};
41400         } else {
41401         */
41402         // not sure what this is..
41403 //            if(!this.tabTpl){
41404                 //this.tabTpl = new Roo.Template(
41405                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41406                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41407                 //);
41408 //                this.tabTpl = new Roo.Template(
41409 //                   '<a href="#">' +
41410 //                   '<span unselectable="on"' +
41411 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41412 //                            ' >{text}</span></a>'
41413 //                );
41414 //                
41415 //            }
41416
41417
41418             var template = tpl || this.tabTpl || false;
41419             
41420             if(!template){
41421                 template =  new Roo.Template(
41422                         Roo.bootstrap.version == 4 ? 
41423                             (
41424                                 '<a class="nav-link" href="#" unselectable="on"' +
41425                                      (this.disableTooltips ? '' : ' title="{text}"') +
41426                                      ' >{text}</a>'
41427                             ) : (
41428                                 '<a class="nav-link" href="#">' +
41429                                 '<span unselectable="on"' +
41430                                          (this.disableTooltips ? '' : ' title="{text}"') +
41431                                     ' >{text}</span></a>'
41432                             )
41433                 );
41434             }
41435             
41436             switch (typeof(template)) {
41437                 case 'object' :
41438                     break;
41439                 case 'string' :
41440                     template = new Roo.Template(template);
41441                     break;
41442                 default :
41443                     break;
41444             }
41445             
41446             var el = template.overwrite(td, {"text": text});
41447             
41448             var inner = el.getElementsByTagName("span")[0];
41449             
41450             return {"el": el, "inner": inner};
41451             
41452     }
41453         
41454     
41455 });
41456
41457 /**
41458  * @class Roo.TabPanelItem
41459  * @extends Roo.util.Observable
41460  * Represents an individual item (tab plus body) in a TabPanel.
41461  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41462  * @param {String} id The id of this TabPanelItem
41463  * @param {String} text The text for the tab of this TabPanelItem
41464  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41465  */
41466 Roo.bootstrap.panel.TabItem = function(config){
41467     /**
41468      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41469      * @type Roo.TabPanel
41470      */
41471     this.tabPanel = config.panel;
41472     /**
41473      * The id for this TabPanelItem
41474      * @type String
41475      */
41476     this.id = config.id;
41477     /** @private */
41478     this.disabled = false;
41479     /** @private */
41480     this.text = config.text;
41481     /** @private */
41482     this.loaded = false;
41483     this.closable = config.closable;
41484
41485     /**
41486      * The body element for this TabPanelItem.
41487      * @type Roo.Element
41488      */
41489     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41490     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41491     this.bodyEl.setStyle("display", "block");
41492     this.bodyEl.setStyle("zoom", "1");
41493     //this.hideAction();
41494
41495     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41496     /** @private */
41497     this.el = Roo.get(els.el);
41498     this.inner = Roo.get(els.inner, true);
41499      this.textEl = Roo.bootstrap.version == 4 ?
41500         this.el : Roo.get(this.el.dom.firstChild, true);
41501
41502     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41503     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41504
41505     
41506 //    this.el.on("mousedown", this.onTabMouseDown, this);
41507     this.el.on("click", this.onTabClick, this);
41508     /** @private */
41509     if(config.closable){
41510         var c = Roo.get(els.close, true);
41511         c.dom.title = this.closeText;
41512         c.addClassOnOver("close-over");
41513         c.on("click", this.closeClick, this);
41514      }
41515
41516     this.addEvents({
41517          /**
41518          * @event activate
41519          * Fires when this tab becomes the active tab.
41520          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41521          * @param {Roo.TabPanelItem} this
41522          */
41523         "activate": true,
41524         /**
41525          * @event beforeclose
41526          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41527          * @param {Roo.TabPanelItem} this
41528          * @param {Object} e Set cancel to true on this object to cancel the close.
41529          */
41530         "beforeclose": true,
41531         /**
41532          * @event close
41533          * Fires when this tab is closed.
41534          * @param {Roo.TabPanelItem} this
41535          */
41536          "close": true,
41537         /**
41538          * @event deactivate
41539          * Fires when this tab is no longer the active tab.
41540          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41541          * @param {Roo.TabPanelItem} this
41542          */
41543          "deactivate" : true
41544     });
41545     this.hidden = false;
41546
41547     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41548 };
41549
41550 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41551            {
41552     purgeListeners : function(){
41553        Roo.util.Observable.prototype.purgeListeners.call(this);
41554        this.el.removeAllListeners();
41555     },
41556     /**
41557      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41558      */
41559     show : function(){
41560         this.status_node.addClass("active");
41561         this.showAction();
41562         if(Roo.isOpera){
41563             this.tabPanel.stripWrap.repaint();
41564         }
41565         this.fireEvent("activate", this.tabPanel, this);
41566     },
41567
41568     /**
41569      * Returns true if this tab is the active tab.
41570      * @return {Boolean}
41571      */
41572     isActive : function(){
41573         return this.tabPanel.getActiveTab() == this;
41574     },
41575
41576     /**
41577      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41578      */
41579     hide : function(){
41580         this.status_node.removeClass("active");
41581         this.hideAction();
41582         this.fireEvent("deactivate", this.tabPanel, this);
41583     },
41584
41585     hideAction : function(){
41586         this.bodyEl.hide();
41587         this.bodyEl.setStyle("position", "absolute");
41588         this.bodyEl.setLeft("-20000px");
41589         this.bodyEl.setTop("-20000px");
41590     },
41591
41592     showAction : function(){
41593         this.bodyEl.setStyle("position", "relative");
41594         this.bodyEl.setTop("");
41595         this.bodyEl.setLeft("");
41596         this.bodyEl.show();
41597     },
41598
41599     /**
41600      * Set the tooltip for the tab.
41601      * @param {String} tooltip The tab's tooltip
41602      */
41603     setTooltip : function(text){
41604         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41605             this.textEl.dom.qtip = text;
41606             this.textEl.dom.removeAttribute('title');
41607         }else{
41608             this.textEl.dom.title = text;
41609         }
41610     },
41611
41612     onTabClick : function(e){
41613         e.preventDefault();
41614         this.tabPanel.activate(this.id);
41615     },
41616
41617     onTabMouseDown : function(e){
41618         e.preventDefault();
41619         this.tabPanel.activate(this.id);
41620     },
41621 /*
41622     getWidth : function(){
41623         return this.inner.getWidth();
41624     },
41625
41626     setWidth : function(width){
41627         var iwidth = width - this.linode.getPadding("lr");
41628         this.inner.setWidth(iwidth);
41629         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41630         this.linode.setWidth(width);
41631     },
41632 */
41633     /**
41634      * Show or hide the tab
41635      * @param {Boolean} hidden True to hide or false to show.
41636      */
41637     setHidden : function(hidden){
41638         this.hidden = hidden;
41639         this.linode.setStyle("display", hidden ? "none" : "");
41640     },
41641
41642     /**
41643      * Returns true if this tab is "hidden"
41644      * @return {Boolean}
41645      */
41646     isHidden : function(){
41647         return this.hidden;
41648     },
41649
41650     /**
41651      * Returns the text for this tab
41652      * @return {String}
41653      */
41654     getText : function(){
41655         return this.text;
41656     },
41657     /*
41658     autoSize : function(){
41659         //this.el.beginMeasure();
41660         this.textEl.setWidth(1);
41661         /*
41662          *  #2804 [new] Tabs in Roojs
41663          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41664          */
41665         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41666         //this.el.endMeasure();
41667     //},
41668
41669     /**
41670      * Sets the text for the tab (Note: this also sets the tooltip text)
41671      * @param {String} text The tab's text and tooltip
41672      */
41673     setText : function(text){
41674         this.text = text;
41675         this.textEl.update(text);
41676         this.setTooltip(text);
41677         //if(!this.tabPanel.resizeTabs){
41678         //    this.autoSize();
41679         //}
41680     },
41681     /**
41682      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41683      */
41684     activate : function(){
41685         this.tabPanel.activate(this.id);
41686     },
41687
41688     /**
41689      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41690      */
41691     disable : function(){
41692         if(this.tabPanel.active != this){
41693             this.disabled = true;
41694             this.status_node.addClass("disabled");
41695         }
41696     },
41697
41698     /**
41699      * Enables this TabPanelItem if it was previously disabled.
41700      */
41701     enable : function(){
41702         this.disabled = false;
41703         this.status_node.removeClass("disabled");
41704     },
41705
41706     /**
41707      * Sets the content for this TabPanelItem.
41708      * @param {String} content The content
41709      * @param {Boolean} loadScripts true to look for and load scripts
41710      */
41711     setContent : function(content, loadScripts){
41712         this.bodyEl.update(content, loadScripts);
41713     },
41714
41715     /**
41716      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41717      * @return {Roo.UpdateManager} The UpdateManager
41718      */
41719     getUpdateManager : function(){
41720         return this.bodyEl.getUpdateManager();
41721     },
41722
41723     /**
41724      * Set a URL to be used to load the content for this TabPanelItem.
41725      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41726      * @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)
41727      * @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)
41728      * @return {Roo.UpdateManager} The UpdateManager
41729      */
41730     setUrl : function(url, params, loadOnce){
41731         if(this.refreshDelegate){
41732             this.un('activate', this.refreshDelegate);
41733         }
41734         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41735         this.on("activate", this.refreshDelegate);
41736         return this.bodyEl.getUpdateManager();
41737     },
41738
41739     /** @private */
41740     _handleRefresh : function(url, params, loadOnce){
41741         if(!loadOnce || !this.loaded){
41742             var updater = this.bodyEl.getUpdateManager();
41743             updater.update(url, params, this._setLoaded.createDelegate(this));
41744         }
41745     },
41746
41747     /**
41748      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41749      *   Will fail silently if the setUrl method has not been called.
41750      *   This does not activate the panel, just updates its content.
41751      */
41752     refresh : function(){
41753         if(this.refreshDelegate){
41754            this.loaded = false;
41755            this.refreshDelegate();
41756         }
41757     },
41758
41759     /** @private */
41760     _setLoaded : function(){
41761         this.loaded = true;
41762     },
41763
41764     /** @private */
41765     closeClick : function(e){
41766         var o = {};
41767         e.stopEvent();
41768         this.fireEvent("beforeclose", this, o);
41769         if(o.cancel !== true){
41770             this.tabPanel.removeTab(this.id);
41771         }
41772     },
41773     /**
41774      * The text displayed in the tooltip for the close icon.
41775      * @type String
41776      */
41777     closeText : "Close this tab"
41778 });
41779 /**
41780 *    This script refer to:
41781 *    Title: International Telephone Input
41782 *    Author: Jack O'Connor
41783 *    Code version:  v12.1.12
41784 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41785 **/
41786
41787 Roo.bootstrap.PhoneInputData = function() {
41788     var d = [
41789       [
41790         "Afghanistan (‫افغانستان‬‎)",
41791         "af",
41792         "93"
41793       ],
41794       [
41795         "Albania (Shqipëri)",
41796         "al",
41797         "355"
41798       ],
41799       [
41800         "Algeria (‫الجزائر‬‎)",
41801         "dz",
41802         "213"
41803       ],
41804       [
41805         "American Samoa",
41806         "as",
41807         "1684"
41808       ],
41809       [
41810         "Andorra",
41811         "ad",
41812         "376"
41813       ],
41814       [
41815         "Angola",
41816         "ao",
41817         "244"
41818       ],
41819       [
41820         "Anguilla",
41821         "ai",
41822         "1264"
41823       ],
41824       [
41825         "Antigua and Barbuda",
41826         "ag",
41827         "1268"
41828       ],
41829       [
41830         "Argentina",
41831         "ar",
41832         "54"
41833       ],
41834       [
41835         "Armenia (Հայաստան)",
41836         "am",
41837         "374"
41838       ],
41839       [
41840         "Aruba",
41841         "aw",
41842         "297"
41843       ],
41844       [
41845         "Australia",
41846         "au",
41847         "61",
41848         0
41849       ],
41850       [
41851         "Austria (Österreich)",
41852         "at",
41853         "43"
41854       ],
41855       [
41856         "Azerbaijan (Azərbaycan)",
41857         "az",
41858         "994"
41859       ],
41860       [
41861         "Bahamas",
41862         "bs",
41863         "1242"
41864       ],
41865       [
41866         "Bahrain (‫البحرين‬‎)",
41867         "bh",
41868         "973"
41869       ],
41870       [
41871         "Bangladesh (বাংলাদেশ)",
41872         "bd",
41873         "880"
41874       ],
41875       [
41876         "Barbados",
41877         "bb",
41878         "1246"
41879       ],
41880       [
41881         "Belarus (Беларусь)",
41882         "by",
41883         "375"
41884       ],
41885       [
41886         "Belgium (België)",
41887         "be",
41888         "32"
41889       ],
41890       [
41891         "Belize",
41892         "bz",
41893         "501"
41894       ],
41895       [
41896         "Benin (Bénin)",
41897         "bj",
41898         "229"
41899       ],
41900       [
41901         "Bermuda",
41902         "bm",
41903         "1441"
41904       ],
41905       [
41906         "Bhutan (འབྲུག)",
41907         "bt",
41908         "975"
41909       ],
41910       [
41911         "Bolivia",
41912         "bo",
41913         "591"
41914       ],
41915       [
41916         "Bosnia and Herzegovina (Босна и Херцеговина)",
41917         "ba",
41918         "387"
41919       ],
41920       [
41921         "Botswana",
41922         "bw",
41923         "267"
41924       ],
41925       [
41926         "Brazil (Brasil)",
41927         "br",
41928         "55"
41929       ],
41930       [
41931         "British Indian Ocean Territory",
41932         "io",
41933         "246"
41934       ],
41935       [
41936         "British Virgin Islands",
41937         "vg",
41938         "1284"
41939       ],
41940       [
41941         "Brunei",
41942         "bn",
41943         "673"
41944       ],
41945       [
41946         "Bulgaria (България)",
41947         "bg",
41948         "359"
41949       ],
41950       [
41951         "Burkina Faso",
41952         "bf",
41953         "226"
41954       ],
41955       [
41956         "Burundi (Uburundi)",
41957         "bi",
41958         "257"
41959       ],
41960       [
41961         "Cambodia (កម្ពុជា)",
41962         "kh",
41963         "855"
41964       ],
41965       [
41966         "Cameroon (Cameroun)",
41967         "cm",
41968         "237"
41969       ],
41970       [
41971         "Canada",
41972         "ca",
41973         "1",
41974         1,
41975         ["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"]
41976       ],
41977       [
41978         "Cape Verde (Kabu Verdi)",
41979         "cv",
41980         "238"
41981       ],
41982       [
41983         "Caribbean Netherlands",
41984         "bq",
41985         "599",
41986         1
41987       ],
41988       [
41989         "Cayman Islands",
41990         "ky",
41991         "1345"
41992       ],
41993       [
41994         "Central African Republic (République centrafricaine)",
41995         "cf",
41996         "236"
41997       ],
41998       [
41999         "Chad (Tchad)",
42000         "td",
42001         "235"
42002       ],
42003       [
42004         "Chile",
42005         "cl",
42006         "56"
42007       ],
42008       [
42009         "China (中国)",
42010         "cn",
42011         "86"
42012       ],
42013       [
42014         "Christmas Island",
42015         "cx",
42016         "61",
42017         2
42018       ],
42019       [
42020         "Cocos (Keeling) Islands",
42021         "cc",
42022         "61",
42023         1
42024       ],
42025       [
42026         "Colombia",
42027         "co",
42028         "57"
42029       ],
42030       [
42031         "Comoros (‫جزر القمر‬‎)",
42032         "km",
42033         "269"
42034       ],
42035       [
42036         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42037         "cd",
42038         "243"
42039       ],
42040       [
42041         "Congo (Republic) (Congo-Brazzaville)",
42042         "cg",
42043         "242"
42044       ],
42045       [
42046         "Cook Islands",
42047         "ck",
42048         "682"
42049       ],
42050       [
42051         "Costa Rica",
42052         "cr",
42053         "506"
42054       ],
42055       [
42056         "Côte d’Ivoire",
42057         "ci",
42058         "225"
42059       ],
42060       [
42061         "Croatia (Hrvatska)",
42062         "hr",
42063         "385"
42064       ],
42065       [
42066         "Cuba",
42067         "cu",
42068         "53"
42069       ],
42070       [
42071         "Curaçao",
42072         "cw",
42073         "599",
42074         0
42075       ],
42076       [
42077         "Cyprus (Κύπρος)",
42078         "cy",
42079         "357"
42080       ],
42081       [
42082         "Czech Republic (Česká republika)",
42083         "cz",
42084         "420"
42085       ],
42086       [
42087         "Denmark (Danmark)",
42088         "dk",
42089         "45"
42090       ],
42091       [
42092         "Djibouti",
42093         "dj",
42094         "253"
42095       ],
42096       [
42097         "Dominica",
42098         "dm",
42099         "1767"
42100       ],
42101       [
42102         "Dominican Republic (República Dominicana)",
42103         "do",
42104         "1",
42105         2,
42106         ["809", "829", "849"]
42107       ],
42108       [
42109         "Ecuador",
42110         "ec",
42111         "593"
42112       ],
42113       [
42114         "Egypt (‫مصر‬‎)",
42115         "eg",
42116         "20"
42117       ],
42118       [
42119         "El Salvador",
42120         "sv",
42121         "503"
42122       ],
42123       [
42124         "Equatorial Guinea (Guinea Ecuatorial)",
42125         "gq",
42126         "240"
42127       ],
42128       [
42129         "Eritrea",
42130         "er",
42131         "291"
42132       ],
42133       [
42134         "Estonia (Eesti)",
42135         "ee",
42136         "372"
42137       ],
42138       [
42139         "Ethiopia",
42140         "et",
42141         "251"
42142       ],
42143       [
42144         "Falkland Islands (Islas Malvinas)",
42145         "fk",
42146         "500"
42147       ],
42148       [
42149         "Faroe Islands (Føroyar)",
42150         "fo",
42151         "298"
42152       ],
42153       [
42154         "Fiji",
42155         "fj",
42156         "679"
42157       ],
42158       [
42159         "Finland (Suomi)",
42160         "fi",
42161         "358",
42162         0
42163       ],
42164       [
42165         "France",
42166         "fr",
42167         "33"
42168       ],
42169       [
42170         "French Guiana (Guyane française)",
42171         "gf",
42172         "594"
42173       ],
42174       [
42175         "French Polynesia (Polynésie française)",
42176         "pf",
42177         "689"
42178       ],
42179       [
42180         "Gabon",
42181         "ga",
42182         "241"
42183       ],
42184       [
42185         "Gambia",
42186         "gm",
42187         "220"
42188       ],
42189       [
42190         "Georgia (საქართველო)",
42191         "ge",
42192         "995"
42193       ],
42194       [
42195         "Germany (Deutschland)",
42196         "de",
42197         "49"
42198       ],
42199       [
42200         "Ghana (Gaana)",
42201         "gh",
42202         "233"
42203       ],
42204       [
42205         "Gibraltar",
42206         "gi",
42207         "350"
42208       ],
42209       [
42210         "Greece (Ελλάδα)",
42211         "gr",
42212         "30"
42213       ],
42214       [
42215         "Greenland (Kalaallit Nunaat)",
42216         "gl",
42217         "299"
42218       ],
42219       [
42220         "Grenada",
42221         "gd",
42222         "1473"
42223       ],
42224       [
42225         "Guadeloupe",
42226         "gp",
42227         "590",
42228         0
42229       ],
42230       [
42231         "Guam",
42232         "gu",
42233         "1671"
42234       ],
42235       [
42236         "Guatemala",
42237         "gt",
42238         "502"
42239       ],
42240       [
42241         "Guernsey",
42242         "gg",
42243         "44",
42244         1
42245       ],
42246       [
42247         "Guinea (Guinée)",
42248         "gn",
42249         "224"
42250       ],
42251       [
42252         "Guinea-Bissau (Guiné Bissau)",
42253         "gw",
42254         "245"
42255       ],
42256       [
42257         "Guyana",
42258         "gy",
42259         "592"
42260       ],
42261       [
42262         "Haiti",
42263         "ht",
42264         "509"
42265       ],
42266       [
42267         "Honduras",
42268         "hn",
42269         "504"
42270       ],
42271       [
42272         "Hong Kong (香港)",
42273         "hk",
42274         "852"
42275       ],
42276       [
42277         "Hungary (Magyarország)",
42278         "hu",
42279         "36"
42280       ],
42281       [
42282         "Iceland (Ísland)",
42283         "is",
42284         "354"
42285       ],
42286       [
42287         "India (भारत)",
42288         "in",
42289         "91"
42290       ],
42291       [
42292         "Indonesia",
42293         "id",
42294         "62"
42295       ],
42296       [
42297         "Iran (‫ایران‬‎)",
42298         "ir",
42299         "98"
42300       ],
42301       [
42302         "Iraq (‫العراق‬‎)",
42303         "iq",
42304         "964"
42305       ],
42306       [
42307         "Ireland",
42308         "ie",
42309         "353"
42310       ],
42311       [
42312         "Isle of Man",
42313         "im",
42314         "44",
42315         2
42316       ],
42317       [
42318         "Israel (‫ישראל‬‎)",
42319         "il",
42320         "972"
42321       ],
42322       [
42323         "Italy (Italia)",
42324         "it",
42325         "39",
42326         0
42327       ],
42328       [
42329         "Jamaica",
42330         "jm",
42331         "1876"
42332       ],
42333       [
42334         "Japan (日本)",
42335         "jp",
42336         "81"
42337       ],
42338       [
42339         "Jersey",
42340         "je",
42341         "44",
42342         3
42343       ],
42344       [
42345         "Jordan (‫الأردن‬‎)",
42346         "jo",
42347         "962"
42348       ],
42349       [
42350         "Kazakhstan (Казахстан)",
42351         "kz",
42352         "7",
42353         1
42354       ],
42355       [
42356         "Kenya",
42357         "ke",
42358         "254"
42359       ],
42360       [
42361         "Kiribati",
42362         "ki",
42363         "686"
42364       ],
42365       [
42366         "Kosovo",
42367         "xk",
42368         "383"
42369       ],
42370       [
42371         "Kuwait (‫الكويت‬‎)",
42372         "kw",
42373         "965"
42374       ],
42375       [
42376         "Kyrgyzstan (Кыргызстан)",
42377         "kg",
42378         "996"
42379       ],
42380       [
42381         "Laos (ລາວ)",
42382         "la",
42383         "856"
42384       ],
42385       [
42386         "Latvia (Latvija)",
42387         "lv",
42388         "371"
42389       ],
42390       [
42391         "Lebanon (‫لبنان‬‎)",
42392         "lb",
42393         "961"
42394       ],
42395       [
42396         "Lesotho",
42397         "ls",
42398         "266"
42399       ],
42400       [
42401         "Liberia",
42402         "lr",
42403         "231"
42404       ],
42405       [
42406         "Libya (‫ليبيا‬‎)",
42407         "ly",
42408         "218"
42409       ],
42410       [
42411         "Liechtenstein",
42412         "li",
42413         "423"
42414       ],
42415       [
42416         "Lithuania (Lietuva)",
42417         "lt",
42418         "370"
42419       ],
42420       [
42421         "Luxembourg",
42422         "lu",
42423         "352"
42424       ],
42425       [
42426         "Macau (澳門)",
42427         "mo",
42428         "853"
42429       ],
42430       [
42431         "Macedonia (FYROM) (Македонија)",
42432         "mk",
42433         "389"
42434       ],
42435       [
42436         "Madagascar (Madagasikara)",
42437         "mg",
42438         "261"
42439       ],
42440       [
42441         "Malawi",
42442         "mw",
42443         "265"
42444       ],
42445       [
42446         "Malaysia",
42447         "my",
42448         "60"
42449       ],
42450       [
42451         "Maldives",
42452         "mv",
42453         "960"
42454       ],
42455       [
42456         "Mali",
42457         "ml",
42458         "223"
42459       ],
42460       [
42461         "Malta",
42462         "mt",
42463         "356"
42464       ],
42465       [
42466         "Marshall Islands",
42467         "mh",
42468         "692"
42469       ],
42470       [
42471         "Martinique",
42472         "mq",
42473         "596"
42474       ],
42475       [
42476         "Mauritania (‫موريتانيا‬‎)",
42477         "mr",
42478         "222"
42479       ],
42480       [
42481         "Mauritius (Moris)",
42482         "mu",
42483         "230"
42484       ],
42485       [
42486         "Mayotte",
42487         "yt",
42488         "262",
42489         1
42490       ],
42491       [
42492         "Mexico (México)",
42493         "mx",
42494         "52"
42495       ],
42496       [
42497         "Micronesia",
42498         "fm",
42499         "691"
42500       ],
42501       [
42502         "Moldova (Republica Moldova)",
42503         "md",
42504         "373"
42505       ],
42506       [
42507         "Monaco",
42508         "mc",
42509         "377"
42510       ],
42511       [
42512         "Mongolia (Монгол)",
42513         "mn",
42514         "976"
42515       ],
42516       [
42517         "Montenegro (Crna Gora)",
42518         "me",
42519         "382"
42520       ],
42521       [
42522         "Montserrat",
42523         "ms",
42524         "1664"
42525       ],
42526       [
42527         "Morocco (‫المغرب‬‎)",
42528         "ma",
42529         "212",
42530         0
42531       ],
42532       [
42533         "Mozambique (Moçambique)",
42534         "mz",
42535         "258"
42536       ],
42537       [
42538         "Myanmar (Burma) (မြန်မာ)",
42539         "mm",
42540         "95"
42541       ],
42542       [
42543         "Namibia (Namibië)",
42544         "na",
42545         "264"
42546       ],
42547       [
42548         "Nauru",
42549         "nr",
42550         "674"
42551       ],
42552       [
42553         "Nepal (नेपाल)",
42554         "np",
42555         "977"
42556       ],
42557       [
42558         "Netherlands (Nederland)",
42559         "nl",
42560         "31"
42561       ],
42562       [
42563         "New Caledonia (Nouvelle-Calédonie)",
42564         "nc",
42565         "687"
42566       ],
42567       [
42568         "New Zealand",
42569         "nz",
42570         "64"
42571       ],
42572       [
42573         "Nicaragua",
42574         "ni",
42575         "505"
42576       ],
42577       [
42578         "Niger (Nijar)",
42579         "ne",
42580         "227"
42581       ],
42582       [
42583         "Nigeria",
42584         "ng",
42585         "234"
42586       ],
42587       [
42588         "Niue",
42589         "nu",
42590         "683"
42591       ],
42592       [
42593         "Norfolk Island",
42594         "nf",
42595         "672"
42596       ],
42597       [
42598         "North Korea (조선 민주주의 인민 공화국)",
42599         "kp",
42600         "850"
42601       ],
42602       [
42603         "Northern Mariana Islands",
42604         "mp",
42605         "1670"
42606       ],
42607       [
42608         "Norway (Norge)",
42609         "no",
42610         "47",
42611         0
42612       ],
42613       [
42614         "Oman (‫عُمان‬‎)",
42615         "om",
42616         "968"
42617       ],
42618       [
42619         "Pakistan (‫پاکستان‬‎)",
42620         "pk",
42621         "92"
42622       ],
42623       [
42624         "Palau",
42625         "pw",
42626         "680"
42627       ],
42628       [
42629         "Palestine (‫فلسطين‬‎)",
42630         "ps",
42631         "970"
42632       ],
42633       [
42634         "Panama (Panamá)",
42635         "pa",
42636         "507"
42637       ],
42638       [
42639         "Papua New Guinea",
42640         "pg",
42641         "675"
42642       ],
42643       [
42644         "Paraguay",
42645         "py",
42646         "595"
42647       ],
42648       [
42649         "Peru (Perú)",
42650         "pe",
42651         "51"
42652       ],
42653       [
42654         "Philippines",
42655         "ph",
42656         "63"
42657       ],
42658       [
42659         "Poland (Polska)",
42660         "pl",
42661         "48"
42662       ],
42663       [
42664         "Portugal",
42665         "pt",
42666         "351"
42667       ],
42668       [
42669         "Puerto Rico",
42670         "pr",
42671         "1",
42672         3,
42673         ["787", "939"]
42674       ],
42675       [
42676         "Qatar (‫قطر‬‎)",
42677         "qa",
42678         "974"
42679       ],
42680       [
42681         "Réunion (La Réunion)",
42682         "re",
42683         "262",
42684         0
42685       ],
42686       [
42687         "Romania (România)",
42688         "ro",
42689         "40"
42690       ],
42691       [
42692         "Russia (Россия)",
42693         "ru",
42694         "7",
42695         0
42696       ],
42697       [
42698         "Rwanda",
42699         "rw",
42700         "250"
42701       ],
42702       [
42703         "Saint Barthélemy",
42704         "bl",
42705         "590",
42706         1
42707       ],
42708       [
42709         "Saint Helena",
42710         "sh",
42711         "290"
42712       ],
42713       [
42714         "Saint Kitts and Nevis",
42715         "kn",
42716         "1869"
42717       ],
42718       [
42719         "Saint Lucia",
42720         "lc",
42721         "1758"
42722       ],
42723       [
42724         "Saint Martin (Saint-Martin (partie française))",
42725         "mf",
42726         "590",
42727         2
42728       ],
42729       [
42730         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42731         "pm",
42732         "508"
42733       ],
42734       [
42735         "Saint Vincent and the Grenadines",
42736         "vc",
42737         "1784"
42738       ],
42739       [
42740         "Samoa",
42741         "ws",
42742         "685"
42743       ],
42744       [
42745         "San Marino",
42746         "sm",
42747         "378"
42748       ],
42749       [
42750         "São Tomé and Príncipe (São Tomé e Príncipe)",
42751         "st",
42752         "239"
42753       ],
42754       [
42755         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42756         "sa",
42757         "966"
42758       ],
42759       [
42760         "Senegal (Sénégal)",
42761         "sn",
42762         "221"
42763       ],
42764       [
42765         "Serbia (Србија)",
42766         "rs",
42767         "381"
42768       ],
42769       [
42770         "Seychelles",
42771         "sc",
42772         "248"
42773       ],
42774       [
42775         "Sierra Leone",
42776         "sl",
42777         "232"
42778       ],
42779       [
42780         "Singapore",
42781         "sg",
42782         "65"
42783       ],
42784       [
42785         "Sint Maarten",
42786         "sx",
42787         "1721"
42788       ],
42789       [
42790         "Slovakia (Slovensko)",
42791         "sk",
42792         "421"
42793       ],
42794       [
42795         "Slovenia (Slovenija)",
42796         "si",
42797         "386"
42798       ],
42799       [
42800         "Solomon Islands",
42801         "sb",
42802         "677"
42803       ],
42804       [
42805         "Somalia (Soomaaliya)",
42806         "so",
42807         "252"
42808       ],
42809       [
42810         "South Africa",
42811         "za",
42812         "27"
42813       ],
42814       [
42815         "South Korea (대한민국)",
42816         "kr",
42817         "82"
42818       ],
42819       [
42820         "South Sudan (‫جنوب السودان‬‎)",
42821         "ss",
42822         "211"
42823       ],
42824       [
42825         "Spain (España)",
42826         "es",
42827         "34"
42828       ],
42829       [
42830         "Sri Lanka (ශ්‍රී ලංකාව)",
42831         "lk",
42832         "94"
42833       ],
42834       [
42835         "Sudan (‫السودان‬‎)",
42836         "sd",
42837         "249"
42838       ],
42839       [
42840         "Suriname",
42841         "sr",
42842         "597"
42843       ],
42844       [
42845         "Svalbard and Jan Mayen",
42846         "sj",
42847         "47",
42848         1
42849       ],
42850       [
42851         "Swaziland",
42852         "sz",
42853         "268"
42854       ],
42855       [
42856         "Sweden (Sverige)",
42857         "se",
42858         "46"
42859       ],
42860       [
42861         "Switzerland (Schweiz)",
42862         "ch",
42863         "41"
42864       ],
42865       [
42866         "Syria (‫سوريا‬‎)",
42867         "sy",
42868         "963"
42869       ],
42870       [
42871         "Taiwan (台灣)",
42872         "tw",
42873         "886"
42874       ],
42875       [
42876         "Tajikistan",
42877         "tj",
42878         "992"
42879       ],
42880       [
42881         "Tanzania",
42882         "tz",
42883         "255"
42884       ],
42885       [
42886         "Thailand (ไทย)",
42887         "th",
42888         "66"
42889       ],
42890       [
42891         "Timor-Leste",
42892         "tl",
42893         "670"
42894       ],
42895       [
42896         "Togo",
42897         "tg",
42898         "228"
42899       ],
42900       [
42901         "Tokelau",
42902         "tk",
42903         "690"
42904       ],
42905       [
42906         "Tonga",
42907         "to",
42908         "676"
42909       ],
42910       [
42911         "Trinidad and Tobago",
42912         "tt",
42913         "1868"
42914       ],
42915       [
42916         "Tunisia (‫تونس‬‎)",
42917         "tn",
42918         "216"
42919       ],
42920       [
42921         "Turkey (Türkiye)",
42922         "tr",
42923         "90"
42924       ],
42925       [
42926         "Turkmenistan",
42927         "tm",
42928         "993"
42929       ],
42930       [
42931         "Turks and Caicos Islands",
42932         "tc",
42933         "1649"
42934       ],
42935       [
42936         "Tuvalu",
42937         "tv",
42938         "688"
42939       ],
42940       [
42941         "U.S. Virgin Islands",
42942         "vi",
42943         "1340"
42944       ],
42945       [
42946         "Uganda",
42947         "ug",
42948         "256"
42949       ],
42950       [
42951         "Ukraine (Україна)",
42952         "ua",
42953         "380"
42954       ],
42955       [
42956         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42957         "ae",
42958         "971"
42959       ],
42960       [
42961         "United Kingdom",
42962         "gb",
42963         "44",
42964         0
42965       ],
42966       [
42967         "United States",
42968         "us",
42969         "1",
42970         0
42971       ],
42972       [
42973         "Uruguay",
42974         "uy",
42975         "598"
42976       ],
42977       [
42978         "Uzbekistan (Oʻzbekiston)",
42979         "uz",
42980         "998"
42981       ],
42982       [
42983         "Vanuatu",
42984         "vu",
42985         "678"
42986       ],
42987       [
42988         "Vatican City (Città del Vaticano)",
42989         "va",
42990         "39",
42991         1
42992       ],
42993       [
42994         "Venezuela",
42995         "ve",
42996         "58"
42997       ],
42998       [
42999         "Vietnam (Việt Nam)",
43000         "vn",
43001         "84"
43002       ],
43003       [
43004         "Wallis and Futuna (Wallis-et-Futuna)",
43005         "wf",
43006         "681"
43007       ],
43008       [
43009         "Western Sahara (‫الصحراء الغربية‬‎)",
43010         "eh",
43011         "212",
43012         1
43013       ],
43014       [
43015         "Yemen (‫اليمن‬‎)",
43016         "ye",
43017         "967"
43018       ],
43019       [
43020         "Zambia",
43021         "zm",
43022         "260"
43023       ],
43024       [
43025         "Zimbabwe",
43026         "zw",
43027         "263"
43028       ],
43029       [
43030         "Åland Islands",
43031         "ax",
43032         "358",
43033         1
43034       ]
43035   ];
43036   
43037   return d;
43038 }/**
43039 *    This script refer to:
43040 *    Title: International Telephone Input
43041 *    Author: Jack O'Connor
43042 *    Code version:  v12.1.12
43043 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43044 **/
43045
43046 /**
43047  * @class Roo.bootstrap.PhoneInput
43048  * @extends Roo.bootstrap.TriggerField
43049  * An input with International dial-code selection
43050  
43051  * @cfg {String} defaultDialCode default '+852'
43052  * @cfg {Array} preferedCountries default []
43053   
43054  * @constructor
43055  * Create a new PhoneInput.
43056  * @param {Object} config Configuration options
43057  */
43058
43059 Roo.bootstrap.PhoneInput = function(config) {
43060     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43061 };
43062
43063 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43064         
43065         listWidth: undefined,
43066         
43067         selectedClass: 'active',
43068         
43069         invalidClass : "has-warning",
43070         
43071         validClass: 'has-success',
43072         
43073         allowed: '0123456789',
43074         
43075         max_length: 15,
43076         
43077         /**
43078          * @cfg {String} defaultDialCode The default dial code when initializing the input
43079          */
43080         defaultDialCode: '+852',
43081         
43082         /**
43083          * @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
43084          */
43085         preferedCountries: false,
43086         
43087         getAutoCreate : function()
43088         {
43089             var data = Roo.bootstrap.PhoneInputData();
43090             var align = this.labelAlign || this.parentLabelAlign();
43091             var id = Roo.id();
43092             
43093             this.allCountries = [];
43094             this.dialCodeMapping = [];
43095             
43096             for (var i = 0; i < data.length; i++) {
43097               var c = data[i];
43098               this.allCountries[i] = {
43099                 name: c[0],
43100                 iso2: c[1],
43101                 dialCode: c[2],
43102                 priority: c[3] || 0,
43103                 areaCodes: c[4] || null
43104               };
43105               this.dialCodeMapping[c[2]] = {
43106                   name: c[0],
43107                   iso2: c[1],
43108                   priority: c[3] || 0,
43109                   areaCodes: c[4] || null
43110               };
43111             }
43112             
43113             var cfg = {
43114                 cls: 'form-group',
43115                 cn: []
43116             };
43117             
43118             var input =  {
43119                 tag: 'input',
43120                 id : id,
43121                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43122                 maxlength: this.max_length,
43123                 cls : 'form-control tel-input',
43124                 autocomplete: 'new-password'
43125             };
43126             
43127             var hiddenInput = {
43128                 tag: 'input',
43129                 type: 'hidden',
43130                 cls: 'hidden-tel-input'
43131             };
43132             
43133             if (this.name) {
43134                 hiddenInput.name = this.name;
43135             }
43136             
43137             if (this.disabled) {
43138                 input.disabled = true;
43139             }
43140             
43141             var flag_container = {
43142                 tag: 'div',
43143                 cls: 'flag-box',
43144                 cn: [
43145                     {
43146                         tag: 'div',
43147                         cls: 'flag'
43148                     },
43149                     {
43150                         tag: 'div',
43151                         cls: 'caret'
43152                     }
43153                 ]
43154             };
43155             
43156             var box = {
43157                 tag: 'div',
43158                 cls: this.hasFeedback ? 'has-feedback' : '',
43159                 cn: [
43160                     hiddenInput,
43161                     input,
43162                     {
43163                         tag: 'input',
43164                         cls: 'dial-code-holder',
43165                         disabled: true
43166                     }
43167                 ]
43168             };
43169             
43170             var container = {
43171                 cls: 'roo-select2-container input-group',
43172                 cn: [
43173                     flag_container,
43174                     box
43175                 ]
43176             };
43177             
43178             if (this.fieldLabel.length) {
43179                 var indicator = {
43180                     tag: 'i',
43181                     tooltip: 'This field is required'
43182                 };
43183                 
43184                 var label = {
43185                     tag: 'label',
43186                     'for':  id,
43187                     cls: 'control-label',
43188                     cn: []
43189                 };
43190                 
43191                 var label_text = {
43192                     tag: 'span',
43193                     html: this.fieldLabel
43194                 };
43195                 
43196                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43197                 label.cn = [
43198                     indicator,
43199                     label_text
43200                 ];
43201                 
43202                 if(this.indicatorpos == 'right') {
43203                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43204                     label.cn = [
43205                         label_text,
43206                         indicator
43207                     ];
43208                 }
43209                 
43210                 if(align == 'left') {
43211                     container = {
43212                         tag: 'div',
43213                         cn: [
43214                             container
43215                         ]
43216                     };
43217                     
43218                     if(this.labelWidth > 12){
43219                         label.style = "width: " + this.labelWidth + 'px';
43220                     }
43221                     if(this.labelWidth < 13 && this.labelmd == 0){
43222                         this.labelmd = this.labelWidth;
43223                     }
43224                     if(this.labellg > 0){
43225                         label.cls += ' col-lg-' + this.labellg;
43226                         input.cls += ' col-lg-' + (12 - this.labellg);
43227                     }
43228                     if(this.labelmd > 0){
43229                         label.cls += ' col-md-' + this.labelmd;
43230                         container.cls += ' col-md-' + (12 - this.labelmd);
43231                     }
43232                     if(this.labelsm > 0){
43233                         label.cls += ' col-sm-' + this.labelsm;
43234                         container.cls += ' col-sm-' + (12 - this.labelsm);
43235                     }
43236                     if(this.labelxs > 0){
43237                         label.cls += ' col-xs-' + this.labelxs;
43238                         container.cls += ' col-xs-' + (12 - this.labelxs);
43239                     }
43240                 }
43241             }
43242             
43243             cfg.cn = [
43244                 label,
43245                 container
43246             ];
43247             
43248             var settings = this;
43249             
43250             ['xs','sm','md','lg'].map(function(size){
43251                 if (settings[size]) {
43252                     cfg.cls += ' col-' + size + '-' + settings[size];
43253                 }
43254             });
43255             
43256             this.store = new Roo.data.Store({
43257                 proxy : new Roo.data.MemoryProxy({}),
43258                 reader : new Roo.data.JsonReader({
43259                     fields : [
43260                         {
43261                             'name' : 'name',
43262                             'type' : 'string'
43263                         },
43264                         {
43265                             'name' : 'iso2',
43266                             'type' : 'string'
43267                         },
43268                         {
43269                             'name' : 'dialCode',
43270                             'type' : 'string'
43271                         },
43272                         {
43273                             'name' : 'priority',
43274                             'type' : 'string'
43275                         },
43276                         {
43277                             'name' : 'areaCodes',
43278                             'type' : 'string'
43279                         }
43280                     ]
43281                 })
43282             });
43283             
43284             if(!this.preferedCountries) {
43285                 this.preferedCountries = [
43286                     'hk',
43287                     'gb',
43288                     'us'
43289                 ];
43290             }
43291             
43292             var p = this.preferedCountries.reverse();
43293             
43294             if(p) {
43295                 for (var i = 0; i < p.length; i++) {
43296                     for (var j = 0; j < this.allCountries.length; j++) {
43297                         if(this.allCountries[j].iso2 == p[i]) {
43298                             var t = this.allCountries[j];
43299                             this.allCountries.splice(j,1);
43300                             this.allCountries.unshift(t);
43301                         }
43302                     } 
43303                 }
43304             }
43305             
43306             this.store.proxy.data = {
43307                 success: true,
43308                 data: this.allCountries
43309             };
43310             
43311             return cfg;
43312         },
43313         
43314         initEvents : function()
43315         {
43316             this.createList();
43317             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43318             
43319             this.indicator = this.indicatorEl();
43320             this.flag = this.flagEl();
43321             this.dialCodeHolder = this.dialCodeHolderEl();
43322             
43323             this.trigger = this.el.select('div.flag-box',true).first();
43324             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43325             
43326             var _this = this;
43327             
43328             (function(){
43329                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43330                 _this.list.setWidth(lw);
43331             }).defer(100);
43332             
43333             this.list.on('mouseover', this.onViewOver, this);
43334             this.list.on('mousemove', this.onViewMove, this);
43335             this.inputEl().on("keyup", this.onKeyUp, this);
43336             this.inputEl().on("keypress", this.onKeyPress, this);
43337             
43338             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43339
43340             this.view = new Roo.View(this.list, this.tpl, {
43341                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43342             });
43343             
43344             this.view.on('click', this.onViewClick, this);
43345             this.setValue(this.defaultDialCode);
43346         },
43347         
43348         onTriggerClick : function(e)
43349         {
43350             Roo.log('trigger click');
43351             if(this.disabled){
43352                 return;
43353             }
43354             
43355             if(this.isExpanded()){
43356                 this.collapse();
43357                 this.hasFocus = false;
43358             }else {
43359                 this.store.load({});
43360                 this.hasFocus = true;
43361                 this.expand();
43362             }
43363         },
43364         
43365         isExpanded : function()
43366         {
43367             return this.list.isVisible();
43368         },
43369         
43370         collapse : function()
43371         {
43372             if(!this.isExpanded()){
43373                 return;
43374             }
43375             this.list.hide();
43376             Roo.get(document).un('mousedown', this.collapseIf, this);
43377             Roo.get(document).un('mousewheel', this.collapseIf, this);
43378             this.fireEvent('collapse', this);
43379             this.validate();
43380         },
43381         
43382         expand : function()
43383         {
43384             Roo.log('expand');
43385
43386             if(this.isExpanded() || !this.hasFocus){
43387                 return;
43388             }
43389             
43390             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43391             this.list.setWidth(lw);
43392             
43393             this.list.show();
43394             this.restrictHeight();
43395             
43396             Roo.get(document).on('mousedown', this.collapseIf, this);
43397             Roo.get(document).on('mousewheel', this.collapseIf, this);
43398             
43399             this.fireEvent('expand', this);
43400         },
43401         
43402         restrictHeight : function()
43403         {
43404             this.list.alignTo(this.inputEl(), this.listAlign);
43405             this.list.alignTo(this.inputEl(), this.listAlign);
43406         },
43407         
43408         onViewOver : function(e, t)
43409         {
43410             if(this.inKeyMode){
43411                 return;
43412             }
43413             var item = this.view.findItemFromChild(t);
43414             
43415             if(item){
43416                 var index = this.view.indexOf(item);
43417                 this.select(index, false);
43418             }
43419         },
43420
43421         // private
43422         onViewClick : function(view, doFocus, el, e)
43423         {
43424             var index = this.view.getSelectedIndexes()[0];
43425             
43426             var r = this.store.getAt(index);
43427             
43428             if(r){
43429                 this.onSelect(r, index);
43430             }
43431             if(doFocus !== false && !this.blockFocus){
43432                 this.inputEl().focus();
43433             }
43434         },
43435         
43436         onViewMove : function(e, t)
43437         {
43438             this.inKeyMode = false;
43439         },
43440         
43441         select : function(index, scrollIntoView)
43442         {
43443             this.selectedIndex = index;
43444             this.view.select(index);
43445             if(scrollIntoView !== false){
43446                 var el = this.view.getNode(index);
43447                 if(el){
43448                     this.list.scrollChildIntoView(el, false);
43449                 }
43450             }
43451         },
43452         
43453         createList : function()
43454         {
43455             this.list = Roo.get(document.body).createChild({
43456                 tag: 'ul',
43457                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43458                 style: 'display:none'
43459             });
43460             
43461             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43462         },
43463         
43464         collapseIf : function(e)
43465         {
43466             var in_combo  = e.within(this.el);
43467             var in_list =  e.within(this.list);
43468             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43469             
43470             if (in_combo || in_list || is_list) {
43471                 return;
43472             }
43473             this.collapse();
43474         },
43475         
43476         onSelect : function(record, index)
43477         {
43478             if(this.fireEvent('beforeselect', this, record, index) !== false){
43479                 
43480                 this.setFlagClass(record.data.iso2);
43481                 this.setDialCode(record.data.dialCode);
43482                 this.hasFocus = false;
43483                 this.collapse();
43484                 this.fireEvent('select', this, record, index);
43485             }
43486         },
43487         
43488         flagEl : function()
43489         {
43490             var flag = this.el.select('div.flag',true).first();
43491             if(!flag){
43492                 return false;
43493             }
43494             return flag;
43495         },
43496         
43497         dialCodeHolderEl : function()
43498         {
43499             var d = this.el.select('input.dial-code-holder',true).first();
43500             if(!d){
43501                 return false;
43502             }
43503             return d;
43504         },
43505         
43506         setDialCode : function(v)
43507         {
43508             this.dialCodeHolder.dom.value = '+'+v;
43509         },
43510         
43511         setFlagClass : function(n)
43512         {
43513             this.flag.dom.className = 'flag '+n;
43514         },
43515         
43516         getValue : function()
43517         {
43518             var v = this.inputEl().getValue();
43519             if(this.dialCodeHolder) {
43520                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43521             }
43522             return v;
43523         },
43524         
43525         setValue : function(v)
43526         {
43527             var d = this.getDialCode(v);
43528             
43529             //invalid dial code
43530             if(v.length == 0 || !d || d.length == 0) {
43531                 if(this.rendered){
43532                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43533                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43534                 }
43535                 return;
43536             }
43537             
43538             //valid dial code
43539             this.setFlagClass(this.dialCodeMapping[d].iso2);
43540             this.setDialCode(d);
43541             this.inputEl().dom.value = v.replace('+'+d,'');
43542             this.hiddenEl().dom.value = this.getValue();
43543             
43544             this.validate();
43545         },
43546         
43547         getDialCode : function(v)
43548         {
43549             v = v ||  '';
43550             
43551             if (v.length == 0) {
43552                 return this.dialCodeHolder.dom.value;
43553             }
43554             
43555             var dialCode = "";
43556             if (v.charAt(0) != "+") {
43557                 return false;
43558             }
43559             var numericChars = "";
43560             for (var i = 1; i < v.length; i++) {
43561               var c = v.charAt(i);
43562               if (!isNaN(c)) {
43563                 numericChars += c;
43564                 if (this.dialCodeMapping[numericChars]) {
43565                   dialCode = v.substr(1, i);
43566                 }
43567                 if (numericChars.length == 4) {
43568                   break;
43569                 }
43570               }
43571             }
43572             return dialCode;
43573         },
43574         
43575         reset : function()
43576         {
43577             this.setValue(this.defaultDialCode);
43578             this.validate();
43579         },
43580         
43581         hiddenEl : function()
43582         {
43583             return this.el.select('input.hidden-tel-input',true).first();
43584         },
43585         
43586         // after setting val
43587         onKeyUp : function(e){
43588             this.setValue(this.getValue());
43589         },
43590         
43591         onKeyPress : function(e){
43592             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43593                 e.stopEvent();
43594             }
43595         }
43596         
43597 });
43598 /**
43599  * @class Roo.bootstrap.MoneyField
43600  * @extends Roo.bootstrap.ComboBox
43601  * Bootstrap MoneyField class
43602  * 
43603  * @constructor
43604  * Create a new MoneyField.
43605  * @param {Object} config Configuration options
43606  */
43607
43608 Roo.bootstrap.MoneyField = function(config) {
43609     
43610     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43611     
43612 };
43613
43614 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43615     
43616     /**
43617      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43618      */
43619     allowDecimals : true,
43620     /**
43621      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43622      */
43623     decimalSeparator : ".",
43624     /**
43625      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43626      */
43627     decimalPrecision : 0,
43628     /**
43629      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43630      */
43631     allowNegative : true,
43632     /**
43633      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43634      */
43635     allowZero: true,
43636     /**
43637      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43638      */
43639     minValue : Number.NEGATIVE_INFINITY,
43640     /**
43641      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43642      */
43643     maxValue : Number.MAX_VALUE,
43644     /**
43645      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43646      */
43647     minText : "The minimum value for this field is {0}",
43648     /**
43649      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43650      */
43651     maxText : "The maximum value for this field is {0}",
43652     /**
43653      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43654      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43655      */
43656     nanText : "{0} is not a valid number",
43657     /**
43658      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43659      */
43660     castInt : true,
43661     /**
43662      * @cfg {String} defaults currency of the MoneyField
43663      * value should be in lkey
43664      */
43665     defaultCurrency : false,
43666     /**
43667      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43668      */
43669     thousandsDelimiter : false,
43670     /**
43671      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43672      */
43673     max_length: false,
43674     
43675     inputlg : 9,
43676     inputmd : 9,
43677     inputsm : 9,
43678     inputxs : 6,
43679     
43680     store : false,
43681     
43682     getAutoCreate : function()
43683     {
43684         var align = this.labelAlign || this.parentLabelAlign();
43685         
43686         var id = Roo.id();
43687
43688         var cfg = {
43689             cls: 'form-group',
43690             cn: []
43691         };
43692
43693         var input =  {
43694             tag: 'input',
43695             id : id,
43696             cls : 'form-control roo-money-amount-input',
43697             autocomplete: 'new-password'
43698         };
43699         
43700         var hiddenInput = {
43701             tag: 'input',
43702             type: 'hidden',
43703             id: Roo.id(),
43704             cls: 'hidden-number-input'
43705         };
43706         
43707         if(this.max_length) {
43708             input.maxlength = this.max_length; 
43709         }
43710         
43711         if (this.name) {
43712             hiddenInput.name = this.name;
43713         }
43714
43715         if (this.disabled) {
43716             input.disabled = true;
43717         }
43718
43719         var clg = 12 - this.inputlg;
43720         var cmd = 12 - this.inputmd;
43721         var csm = 12 - this.inputsm;
43722         var cxs = 12 - this.inputxs;
43723         
43724         var container = {
43725             tag : 'div',
43726             cls : 'row roo-money-field',
43727             cn : [
43728                 {
43729                     tag : 'div',
43730                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43731                     cn : [
43732                         {
43733                             tag : 'div',
43734                             cls: 'roo-select2-container input-group',
43735                             cn: [
43736                                 {
43737                                     tag : 'input',
43738                                     cls : 'form-control roo-money-currency-input',
43739                                     autocomplete: 'new-password',
43740                                     readOnly : 1,
43741                                     name : this.currencyName
43742                                 },
43743                                 {
43744                                     tag :'span',
43745                                     cls : 'input-group-addon',
43746                                     cn : [
43747                                         {
43748                                             tag: 'span',
43749                                             cls: 'caret'
43750                                         }
43751                                     ]
43752                                 }
43753                             ]
43754                         }
43755                     ]
43756                 },
43757                 {
43758                     tag : 'div',
43759                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43760                     cn : [
43761                         {
43762                             tag: 'div',
43763                             cls: this.hasFeedback ? 'has-feedback' : '',
43764                             cn: [
43765                                 input
43766                             ]
43767                         }
43768                     ]
43769                 }
43770             ]
43771             
43772         };
43773         
43774         if (this.fieldLabel.length) {
43775             var indicator = {
43776                 tag: 'i',
43777                 tooltip: 'This field is required'
43778             };
43779
43780             var label = {
43781                 tag: 'label',
43782                 'for':  id,
43783                 cls: 'control-label',
43784                 cn: []
43785             };
43786
43787             var label_text = {
43788                 tag: 'span',
43789                 html: this.fieldLabel
43790             };
43791
43792             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43793             label.cn = [
43794                 indicator,
43795                 label_text
43796             ];
43797
43798             if(this.indicatorpos == 'right') {
43799                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43800                 label.cn = [
43801                     label_text,
43802                     indicator
43803                 ];
43804             }
43805
43806             if(align == 'left') {
43807                 container = {
43808                     tag: 'div',
43809                     cn: [
43810                         container
43811                     ]
43812                 };
43813
43814                 if(this.labelWidth > 12){
43815                     label.style = "width: " + this.labelWidth + 'px';
43816                 }
43817                 if(this.labelWidth < 13 && this.labelmd == 0){
43818                     this.labelmd = this.labelWidth;
43819                 }
43820                 if(this.labellg > 0){
43821                     label.cls += ' col-lg-' + this.labellg;
43822                     input.cls += ' col-lg-' + (12 - this.labellg);
43823                 }
43824                 if(this.labelmd > 0){
43825                     label.cls += ' col-md-' + this.labelmd;
43826                     container.cls += ' col-md-' + (12 - this.labelmd);
43827                 }
43828                 if(this.labelsm > 0){
43829                     label.cls += ' col-sm-' + this.labelsm;
43830                     container.cls += ' col-sm-' + (12 - this.labelsm);
43831                 }
43832                 if(this.labelxs > 0){
43833                     label.cls += ' col-xs-' + this.labelxs;
43834                     container.cls += ' col-xs-' + (12 - this.labelxs);
43835                 }
43836             }
43837         }
43838
43839         cfg.cn = [
43840             label,
43841             container,
43842             hiddenInput
43843         ];
43844         
43845         var settings = this;
43846
43847         ['xs','sm','md','lg'].map(function(size){
43848             if (settings[size]) {
43849                 cfg.cls += ' col-' + size + '-' + settings[size];
43850             }
43851         });
43852         
43853         return cfg;
43854     },
43855     
43856     initEvents : function()
43857     {
43858         this.indicator = this.indicatorEl();
43859         
43860         this.initCurrencyEvent();
43861         
43862         this.initNumberEvent();
43863     },
43864     
43865     initCurrencyEvent : function()
43866     {
43867         if (!this.store) {
43868             throw "can not find store for combo";
43869         }
43870         
43871         this.store = Roo.factory(this.store, Roo.data);
43872         this.store.parent = this;
43873         
43874         this.createList();
43875         
43876         this.triggerEl = this.el.select('.input-group-addon', true).first();
43877         
43878         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43879         
43880         var _this = this;
43881         
43882         (function(){
43883             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43884             _this.list.setWidth(lw);
43885         }).defer(100);
43886         
43887         this.list.on('mouseover', this.onViewOver, this);
43888         this.list.on('mousemove', this.onViewMove, this);
43889         this.list.on('scroll', this.onViewScroll, this);
43890         
43891         if(!this.tpl){
43892             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43893         }
43894         
43895         this.view = new Roo.View(this.list, this.tpl, {
43896             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43897         });
43898         
43899         this.view.on('click', this.onViewClick, this);
43900         
43901         this.store.on('beforeload', this.onBeforeLoad, this);
43902         this.store.on('load', this.onLoad, this);
43903         this.store.on('loadexception', this.onLoadException, this);
43904         
43905         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43906             "up" : function(e){
43907                 this.inKeyMode = true;
43908                 this.selectPrev();
43909             },
43910
43911             "down" : function(e){
43912                 if(!this.isExpanded()){
43913                     this.onTriggerClick();
43914                 }else{
43915                     this.inKeyMode = true;
43916                     this.selectNext();
43917                 }
43918             },
43919
43920             "enter" : function(e){
43921                 this.collapse();
43922                 
43923                 if(this.fireEvent("specialkey", this, e)){
43924                     this.onViewClick(false);
43925                 }
43926                 
43927                 return true;
43928             },
43929
43930             "esc" : function(e){
43931                 this.collapse();
43932             },
43933
43934             "tab" : function(e){
43935                 this.collapse();
43936                 
43937                 if(this.fireEvent("specialkey", this, e)){
43938                     this.onViewClick(false);
43939                 }
43940                 
43941                 return true;
43942             },
43943
43944             scope : this,
43945
43946             doRelay : function(foo, bar, hname){
43947                 if(hname == 'down' || this.scope.isExpanded()){
43948                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43949                 }
43950                 return true;
43951             },
43952
43953             forceKeyDown: true
43954         });
43955         
43956         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43957         
43958     },
43959     
43960     initNumberEvent : function(e)
43961     {
43962         this.inputEl().on("keydown" , this.fireKey,  this);
43963         this.inputEl().on("focus", this.onFocus,  this);
43964         this.inputEl().on("blur", this.onBlur,  this);
43965         
43966         this.inputEl().relayEvent('keyup', this);
43967         
43968         if(this.indicator){
43969             this.indicator.addClass('invisible');
43970         }
43971  
43972         this.originalValue = this.getValue();
43973         
43974         if(this.validationEvent == 'keyup'){
43975             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43976             this.inputEl().on('keyup', this.filterValidation, this);
43977         }
43978         else if(this.validationEvent !== false){
43979             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43980         }
43981         
43982         if(this.selectOnFocus){
43983             this.on("focus", this.preFocus, this);
43984             
43985         }
43986         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43987             this.inputEl().on("keypress", this.filterKeys, this);
43988         } else {
43989             this.inputEl().relayEvent('keypress', this);
43990         }
43991         
43992         var allowed = "0123456789";
43993         
43994         if(this.allowDecimals){
43995             allowed += this.decimalSeparator;
43996         }
43997         
43998         if(this.allowNegative){
43999             allowed += "-";
44000         }
44001         
44002         if(this.thousandsDelimiter) {
44003             allowed += ",";
44004         }
44005         
44006         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44007         
44008         var keyPress = function(e){
44009             
44010             var k = e.getKey();
44011             
44012             var c = e.getCharCode();
44013             
44014             if(
44015                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44016                     allowed.indexOf(String.fromCharCode(c)) === -1
44017             ){
44018                 e.stopEvent();
44019                 return;
44020             }
44021             
44022             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44023                 return;
44024             }
44025             
44026             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44027                 e.stopEvent();
44028             }
44029         };
44030         
44031         this.inputEl().on("keypress", keyPress, this);
44032         
44033     },
44034     
44035     onTriggerClick : function(e)
44036     {   
44037         if(this.disabled){
44038             return;
44039         }
44040         
44041         this.page = 0;
44042         this.loadNext = false;
44043         
44044         if(this.isExpanded()){
44045             this.collapse();
44046             return;
44047         }
44048         
44049         this.hasFocus = true;
44050         
44051         if(this.triggerAction == 'all') {
44052             this.doQuery(this.allQuery, true);
44053             return;
44054         }
44055         
44056         this.doQuery(this.getRawValue());
44057     },
44058     
44059     getCurrency : function()
44060     {   
44061         var v = this.currencyEl().getValue();
44062         
44063         return v;
44064     },
44065     
44066     restrictHeight : function()
44067     {
44068         this.list.alignTo(this.currencyEl(), this.listAlign);
44069         this.list.alignTo(this.currencyEl(), this.listAlign);
44070     },
44071     
44072     onViewClick : function(view, doFocus, el, e)
44073     {
44074         var index = this.view.getSelectedIndexes()[0];
44075         
44076         var r = this.store.getAt(index);
44077         
44078         if(r){
44079             this.onSelect(r, index);
44080         }
44081     },
44082     
44083     onSelect : function(record, index){
44084         
44085         if(this.fireEvent('beforeselect', this, record, index) !== false){
44086         
44087             this.setFromCurrencyData(index > -1 ? record.data : false);
44088             
44089             this.collapse();
44090             
44091             this.fireEvent('select', this, record, index);
44092         }
44093     },
44094     
44095     setFromCurrencyData : function(o)
44096     {
44097         var currency = '';
44098         
44099         this.lastCurrency = o;
44100         
44101         if (this.currencyField) {
44102             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44103         } else {
44104             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44105         }
44106         
44107         this.lastSelectionText = currency;
44108         
44109         //setting default currency
44110         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44111             this.setCurrency(this.defaultCurrency);
44112             return;
44113         }
44114         
44115         this.setCurrency(currency);
44116     },
44117     
44118     setFromData : function(o)
44119     {
44120         var c = {};
44121         
44122         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44123         
44124         this.setFromCurrencyData(c);
44125         
44126         var value = '';
44127         
44128         if (this.name) {
44129             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44130         } else {
44131             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44132         }
44133         
44134         this.setValue(value);
44135         
44136     },
44137     
44138     setCurrency : function(v)
44139     {   
44140         this.currencyValue = v;
44141         
44142         if(this.rendered){
44143             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44144             this.validate();
44145         }
44146     },
44147     
44148     setValue : function(v)
44149     {
44150         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44151         
44152         this.value = v;
44153         
44154         if(this.rendered){
44155             
44156             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44157             
44158             this.inputEl().dom.value = (v == '') ? '' :
44159                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44160             
44161             if(!this.allowZero && v === '0') {
44162                 this.hiddenEl().dom.value = '';
44163                 this.inputEl().dom.value = '';
44164             }
44165             
44166             this.validate();
44167         }
44168     },
44169     
44170     getRawValue : function()
44171     {
44172         var v = this.inputEl().getValue();
44173         
44174         return v;
44175     },
44176     
44177     getValue : function()
44178     {
44179         return this.fixPrecision(this.parseValue(this.getRawValue()));
44180     },
44181     
44182     parseValue : function(value)
44183     {
44184         if(this.thousandsDelimiter) {
44185             value += "";
44186             r = new RegExp(",", "g");
44187             value = value.replace(r, "");
44188         }
44189         
44190         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44191         return isNaN(value) ? '' : value;
44192         
44193     },
44194     
44195     fixPrecision : function(value)
44196     {
44197         if(this.thousandsDelimiter) {
44198             value += "";
44199             r = new RegExp(",", "g");
44200             value = value.replace(r, "");
44201         }
44202         
44203         var nan = isNaN(value);
44204         
44205         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44206             return nan ? '' : value;
44207         }
44208         return parseFloat(value).toFixed(this.decimalPrecision);
44209     },
44210     
44211     decimalPrecisionFcn : function(v)
44212     {
44213         return Math.floor(v);
44214     },
44215     
44216     validateValue : function(value)
44217     {
44218         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44219             return false;
44220         }
44221         
44222         var num = this.parseValue(value);
44223         
44224         if(isNaN(num)){
44225             this.markInvalid(String.format(this.nanText, value));
44226             return false;
44227         }
44228         
44229         if(num < this.minValue){
44230             this.markInvalid(String.format(this.minText, this.minValue));
44231             return false;
44232         }
44233         
44234         if(num > this.maxValue){
44235             this.markInvalid(String.format(this.maxText, this.maxValue));
44236             return false;
44237         }
44238         
44239         return true;
44240     },
44241     
44242     validate : function()
44243     {
44244         if(this.disabled || this.allowBlank){
44245             this.markValid();
44246             return true;
44247         }
44248         
44249         var currency = this.getCurrency();
44250         
44251         if(this.validateValue(this.getRawValue()) && currency.length){
44252             this.markValid();
44253             return true;
44254         }
44255         
44256         this.markInvalid();
44257         return false;
44258     },
44259     
44260     getName: function()
44261     {
44262         return this.name;
44263     },
44264     
44265     beforeBlur : function()
44266     {
44267         if(!this.castInt){
44268             return;
44269         }
44270         
44271         var v = this.parseValue(this.getRawValue());
44272         
44273         if(v || v == 0){
44274             this.setValue(v);
44275         }
44276     },
44277     
44278     onBlur : function()
44279     {
44280         this.beforeBlur();
44281         
44282         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44283             //this.el.removeClass(this.focusClass);
44284         }
44285         
44286         this.hasFocus = false;
44287         
44288         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44289             this.validate();
44290         }
44291         
44292         var v = this.getValue();
44293         
44294         if(String(v) !== String(this.startValue)){
44295             this.fireEvent('change', this, v, this.startValue);
44296         }
44297         
44298         this.fireEvent("blur", this);
44299     },
44300     
44301     inputEl : function()
44302     {
44303         return this.el.select('.roo-money-amount-input', true).first();
44304     },
44305     
44306     currencyEl : function()
44307     {
44308         return this.el.select('.roo-money-currency-input', true).first();
44309     },
44310     
44311     hiddenEl : function()
44312     {
44313         return this.el.select('input.hidden-number-input',true).first();
44314     }
44315     
44316 });/**
44317  * @class Roo.bootstrap.BezierSignature
44318  * @extends Roo.bootstrap.Component
44319  * Bootstrap BezierSignature class
44320  * This script refer to:
44321  *    Title: Signature Pad
44322  *    Author: szimek
44323  *    Availability: https://github.com/szimek/signature_pad
44324  *
44325  * @constructor
44326  * Create a new BezierSignature
44327  * @param {Object} config The config object
44328  */
44329
44330 Roo.bootstrap.BezierSignature = function(config){
44331     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44332     this.addEvents({
44333         "resize" : true
44334     });
44335 };
44336
44337 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44338 {
44339      
44340     curve_data: [],
44341     
44342     is_empty: true,
44343     
44344     mouse_btn_down: true,
44345     
44346     /**
44347      * @cfg {int} canvas height
44348      */
44349     canvas_height: '200px',
44350     
44351     /**
44352      * @cfg {float|function} Radius of a single dot.
44353      */ 
44354     dot_size: false,
44355     
44356     /**
44357      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44358      */
44359     min_width: 0.5,
44360     
44361     /**
44362      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44363      */
44364     max_width: 2.5,
44365     
44366     /**
44367      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44368      */
44369     throttle: 16,
44370     
44371     /**
44372      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44373      */
44374     min_distance: 5,
44375     
44376     /**
44377      * @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.
44378      */
44379     bg_color: 'rgba(0, 0, 0, 0)',
44380     
44381     /**
44382      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44383      */
44384     dot_color: 'black',
44385     
44386     /**
44387      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44388      */ 
44389     velocity_filter_weight: 0.7,
44390     
44391     /**
44392      * @cfg {function} Callback when stroke begin. 
44393      */
44394     onBegin: false,
44395     
44396     /**
44397      * @cfg {function} Callback when stroke end.
44398      */
44399     onEnd: false,
44400     
44401     getAutoCreate : function()
44402     {
44403         var cls = 'roo-signature column';
44404         
44405         if(this.cls){
44406             cls += ' ' + this.cls;
44407         }
44408         
44409         var col_sizes = [
44410             'lg',
44411             'md',
44412             'sm',
44413             'xs'
44414         ];
44415         
44416         for(var i = 0; i < col_sizes.length; i++) {
44417             if(this[col_sizes[i]]) {
44418                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44419             }
44420         }
44421         
44422         var cfg = {
44423             tag: 'div',
44424             cls: cls,
44425             cn: [
44426                 {
44427                     tag: 'div',
44428                     cls: 'roo-signature-body',
44429                     cn: [
44430                         {
44431                             tag: 'canvas',
44432                             cls: 'roo-signature-body-canvas',
44433                             height: this.canvas_height,
44434                             width: this.canvas_width
44435                         }
44436                     ]
44437                 },
44438                 {
44439                     tag: 'input',
44440                     type: 'file',
44441                     style: 'display: none'
44442                 }
44443             ]
44444         };
44445         
44446         return cfg;
44447     },
44448     
44449     initEvents: function() 
44450     {
44451         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44452         
44453         var canvas = this.canvasEl();
44454         
44455         // mouse && touch event swapping...
44456         canvas.dom.style.touchAction = 'none';
44457         canvas.dom.style.msTouchAction = 'none';
44458         
44459         this.mouse_btn_down = false;
44460         canvas.on('mousedown', this._handleMouseDown, this);
44461         canvas.on('mousemove', this._handleMouseMove, this);
44462         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44463         
44464         if (window.PointerEvent) {
44465             canvas.on('pointerdown', this._handleMouseDown, this);
44466             canvas.on('pointermove', this._handleMouseMove, this);
44467             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44468         }
44469         
44470         if ('ontouchstart' in window) {
44471             canvas.on('touchstart', this._handleTouchStart, this);
44472             canvas.on('touchmove', this._handleTouchMove, this);
44473             canvas.on('touchend', this._handleTouchEnd, this);
44474         }
44475         
44476         Roo.EventManager.onWindowResize(this.resize, this, true);
44477         
44478         // file input event
44479         this.fileEl().on('change', this.uploadImage, this);
44480         
44481         this.clear();
44482         
44483         this.resize();
44484     },
44485     
44486     resize: function(){
44487         
44488         var canvas = this.canvasEl().dom;
44489         var ctx = this.canvasElCtx();
44490         var img_data = false;
44491         
44492         if(canvas.width > 0) {
44493             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44494         }
44495         // setting canvas width will clean img data
44496         canvas.width = 0;
44497         
44498         var style = window.getComputedStyle ? 
44499             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44500             
44501         var padding_left = parseInt(style.paddingLeft) || 0;
44502         var padding_right = parseInt(style.paddingRight) || 0;
44503         
44504         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44505         
44506         if(img_data) {
44507             ctx.putImageData(img_data, 0, 0);
44508         }
44509     },
44510     
44511     _handleMouseDown: function(e)
44512     {
44513         if (e.browserEvent.which === 1) {
44514             this.mouse_btn_down = true;
44515             this.strokeBegin(e);
44516         }
44517     },
44518     
44519     _handleMouseMove: function (e)
44520     {
44521         if (this.mouse_btn_down) {
44522             this.strokeMoveUpdate(e);
44523         }
44524     },
44525     
44526     _handleMouseUp: function (e)
44527     {
44528         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44529             this.mouse_btn_down = false;
44530             this.strokeEnd(e);
44531         }
44532     },
44533     
44534     _handleTouchStart: function (e) {
44535         
44536         e.preventDefault();
44537         if (e.browserEvent.targetTouches.length === 1) {
44538             // var touch = e.browserEvent.changedTouches[0];
44539             // this.strokeBegin(touch);
44540             
44541              this.strokeBegin(e); // assume e catching the correct xy...
44542         }
44543     },
44544     
44545     _handleTouchMove: function (e) {
44546         e.preventDefault();
44547         // var touch = event.targetTouches[0];
44548         // _this._strokeMoveUpdate(touch);
44549         this.strokeMoveUpdate(e);
44550     },
44551     
44552     _handleTouchEnd: function (e) {
44553         var wasCanvasTouched = e.target === this.canvasEl().dom;
44554         if (wasCanvasTouched) {
44555             e.preventDefault();
44556             // var touch = event.changedTouches[0];
44557             // _this._strokeEnd(touch);
44558             this.strokeEnd(e);
44559         }
44560     },
44561     
44562     reset: function () {
44563         this._lastPoints = [];
44564         this._lastVelocity = 0;
44565         this._lastWidth = (this.min_width + this.max_width) / 2;
44566         this.canvasElCtx().fillStyle = this.dot_color;
44567     },
44568     
44569     strokeMoveUpdate: function(e)
44570     {
44571         this.strokeUpdate(e);
44572         
44573         if (this.throttle) {
44574             this.throttleStroke(this.strokeUpdate, this.throttle);
44575         }
44576         else {
44577             this.strokeUpdate(e);
44578         }
44579     },
44580     
44581     strokeBegin: function(e)
44582     {
44583         var newPointGroup = {
44584             color: this.dot_color,
44585             points: []
44586         };
44587         
44588         if (typeof this.onBegin === 'function') {
44589             this.onBegin(e);
44590         }
44591         
44592         this.curve_data.push(newPointGroup);
44593         this.reset();
44594         this.strokeUpdate(e);
44595     },
44596     
44597     strokeUpdate: function(e)
44598     {
44599         var rect = this.canvasEl().dom.getBoundingClientRect();
44600         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44601         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44602         var lastPoints = lastPointGroup.points;
44603         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44604         var isLastPointTooClose = lastPoint
44605             ? point.distanceTo(lastPoint) <= this.min_distance
44606             : false;
44607         var color = lastPointGroup.color;
44608         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44609             var curve = this.addPoint(point);
44610             if (!lastPoint) {
44611                 this.drawDot({color: color, point: point});
44612             }
44613             else if (curve) {
44614                 this.drawCurve({color: color, curve: curve});
44615             }
44616             lastPoints.push({
44617                 time: point.time,
44618                 x: point.x,
44619                 y: point.y
44620             });
44621         }
44622     },
44623     
44624     strokeEnd: function(e)
44625     {
44626         this.strokeUpdate(e);
44627         if (typeof this.onEnd === 'function') {
44628             this.onEnd(e);
44629         }
44630     },
44631     
44632     addPoint:  function (point) {
44633         var _lastPoints = this._lastPoints;
44634         _lastPoints.push(point);
44635         if (_lastPoints.length > 2) {
44636             if (_lastPoints.length === 3) {
44637                 _lastPoints.unshift(_lastPoints[0]);
44638             }
44639             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44640             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44641             _lastPoints.shift();
44642             return curve;
44643         }
44644         return null;
44645     },
44646     
44647     calculateCurveWidths: function (startPoint, endPoint) {
44648         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44649             (1 - this.velocity_filter_weight) * this._lastVelocity;
44650
44651         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44652         var widths = {
44653             end: newWidth,
44654             start: this._lastWidth
44655         };
44656         
44657         this._lastVelocity = velocity;
44658         this._lastWidth = newWidth;
44659         return widths;
44660     },
44661     
44662     drawDot: function (_a) {
44663         var color = _a.color, point = _a.point;
44664         var ctx = this.canvasElCtx();
44665         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44666         ctx.beginPath();
44667         this.drawCurveSegment(point.x, point.y, width);
44668         ctx.closePath();
44669         ctx.fillStyle = color;
44670         ctx.fill();
44671     },
44672     
44673     drawCurve: function (_a) {
44674         var color = _a.color, curve = _a.curve;
44675         var ctx = this.canvasElCtx();
44676         var widthDelta = curve.endWidth - curve.startWidth;
44677         var drawSteps = Math.floor(curve.length()) * 2;
44678         ctx.beginPath();
44679         ctx.fillStyle = color;
44680         for (var i = 0; i < drawSteps; i += 1) {
44681         var t = i / drawSteps;
44682         var tt = t * t;
44683         var ttt = tt * t;
44684         var u = 1 - t;
44685         var uu = u * u;
44686         var uuu = uu * u;
44687         var x = uuu * curve.startPoint.x;
44688         x += 3 * uu * t * curve.control1.x;
44689         x += 3 * u * tt * curve.control2.x;
44690         x += ttt * curve.endPoint.x;
44691         var y = uuu * curve.startPoint.y;
44692         y += 3 * uu * t * curve.control1.y;
44693         y += 3 * u * tt * curve.control2.y;
44694         y += ttt * curve.endPoint.y;
44695         var width = curve.startWidth + ttt * widthDelta;
44696         this.drawCurveSegment(x, y, width);
44697         }
44698         ctx.closePath();
44699         ctx.fill();
44700     },
44701     
44702     drawCurveSegment: function (x, y, width) {
44703         var ctx = this.canvasElCtx();
44704         ctx.moveTo(x, y);
44705         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44706         this.is_empty = false;
44707     },
44708     
44709     clear: function()
44710     {
44711         var ctx = this.canvasElCtx();
44712         var canvas = this.canvasEl().dom;
44713         ctx.fillStyle = this.bg_color;
44714         ctx.clearRect(0, 0, canvas.width, canvas.height);
44715         ctx.fillRect(0, 0, canvas.width, canvas.height);
44716         this.curve_data = [];
44717         this.reset();
44718         this.is_empty = true;
44719     },
44720     
44721     fileEl: function()
44722     {
44723         return  this.el.select('input',true).first();
44724     },
44725     
44726     canvasEl: function()
44727     {
44728         return this.el.select('canvas',true).first();
44729     },
44730     
44731     canvasElCtx: function()
44732     {
44733         return this.el.select('canvas',true).first().dom.getContext('2d');
44734     },
44735     
44736     getImage: function(type)
44737     {
44738         if(this.is_empty) {
44739             return false;
44740         }
44741         
44742         // encryption ?
44743         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44744     },
44745     
44746     drawFromImage: function(img_src)
44747     {
44748         var img = new Image();
44749         
44750         img.onload = function(){
44751             this.canvasElCtx().drawImage(img, 0, 0);
44752         }.bind(this);
44753         
44754         img.src = img_src;
44755         
44756         this.is_empty = false;
44757     },
44758     
44759     selectImage: function()
44760     {
44761         this.fileEl().dom.click();
44762     },
44763     
44764     uploadImage: function(e)
44765     {
44766         var reader = new FileReader();
44767         
44768         reader.onload = function(e){
44769             var img = new Image();
44770             img.onload = function(){
44771                 this.reset();
44772                 this.canvasElCtx().drawImage(img, 0, 0);
44773             }.bind(this);
44774             img.src = e.target.result;
44775         }.bind(this);
44776         
44777         reader.readAsDataURL(e.target.files[0]);
44778     },
44779     
44780     // Bezier Point Constructor
44781     Point: (function () {
44782         function Point(x, y, time) {
44783             this.x = x;
44784             this.y = y;
44785             this.time = time || Date.now();
44786         }
44787         Point.prototype.distanceTo = function (start) {
44788             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44789         };
44790         Point.prototype.equals = function (other) {
44791             return this.x === other.x && this.y === other.y && this.time === other.time;
44792         };
44793         Point.prototype.velocityFrom = function (start) {
44794             return this.time !== start.time
44795             ? this.distanceTo(start) / (this.time - start.time)
44796             : 0;
44797         };
44798         return Point;
44799     }()),
44800     
44801     
44802     // Bezier Constructor
44803     Bezier: (function () {
44804         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44805             this.startPoint = startPoint;
44806             this.control2 = control2;
44807             this.control1 = control1;
44808             this.endPoint = endPoint;
44809             this.startWidth = startWidth;
44810             this.endWidth = endWidth;
44811         }
44812         Bezier.fromPoints = function (points, widths, scope) {
44813             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44814             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44815             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44816         };
44817         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44818             var dx1 = s1.x - s2.x;
44819             var dy1 = s1.y - s2.y;
44820             var dx2 = s2.x - s3.x;
44821             var dy2 = s2.y - s3.y;
44822             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44823             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44824             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44825             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44826             var dxm = m1.x - m2.x;
44827             var dym = m1.y - m2.y;
44828             var k = l2 / (l1 + l2);
44829             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44830             var tx = s2.x - cm.x;
44831             var ty = s2.y - cm.y;
44832             return {
44833                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44834                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44835             };
44836         };
44837         Bezier.prototype.length = function () {
44838             var steps = 10;
44839             var length = 0;
44840             var px;
44841             var py;
44842             for (var i = 0; i <= steps; i += 1) {
44843                 var t = i / steps;
44844                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44845                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44846                 if (i > 0) {
44847                     var xdiff = cx - px;
44848                     var ydiff = cy - py;
44849                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44850                 }
44851                 px = cx;
44852                 py = cy;
44853             }
44854             return length;
44855         };
44856         Bezier.prototype.point = function (t, start, c1, c2, end) {
44857             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44858             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44859             + (3.0 * c2 * (1.0 - t) * t * t)
44860             + (end * t * t * t);
44861         };
44862         return Bezier;
44863     }()),
44864     
44865     throttleStroke: function(fn, wait) {
44866       if (wait === void 0) { wait = 250; }
44867       var previous = 0;
44868       var timeout = null;
44869       var result;
44870       var storedContext;
44871       var storedArgs;
44872       var later = function () {
44873           previous = Date.now();
44874           timeout = null;
44875           result = fn.apply(storedContext, storedArgs);
44876           if (!timeout) {
44877               storedContext = null;
44878               storedArgs = [];
44879           }
44880       };
44881       return function wrapper() {
44882           var args = [];
44883           for (var _i = 0; _i < arguments.length; _i++) {
44884               args[_i] = arguments[_i];
44885           }
44886           var now = Date.now();
44887           var remaining = wait - (now - previous);
44888           storedContext = this;
44889           storedArgs = args;
44890           if (remaining <= 0 || remaining > wait) {
44891               if (timeout) {
44892                   clearTimeout(timeout);
44893                   timeout = null;
44894               }
44895               previous = now;
44896               result = fn.apply(storedContext, storedArgs);
44897               if (!timeout) {
44898                   storedContext = null;
44899                   storedArgs = [];
44900               }
44901           }
44902           else if (!timeout) {
44903               timeout = window.setTimeout(later, remaining);
44904           }
44905           return result;
44906       };
44907   }
44908   
44909 });
44910
44911  
44912
44913