Fix #6874 - Grid column resize
[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 = (typeof(cfg.style) == 'undefined' ? this.style : 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  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
3257  * @class Roo.bootstrap.Link
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Link Class
3260  * @cfg {String} alt image alternative text
3261  * @cfg {String} href a tag href
3262  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263  * @cfg {String} html the content of the link.
3264  * @cfg {String} anchor name for the anchor link
3265  * @cfg {String} fa - favicon
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Header
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Header class
3364  * @cfg {String} html content of header
3365  * @cfg {Number} level (1|2|3|4|5|6) default 1
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
3413  * @class Roo.bootstrap.MenuMgr
3414  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
3462           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463           m.parentMenu.activeChild = m;
3464        }else if(last && last.isVisible()){
3465           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
3529            menus[menu.id] = menu;
3530            menu.on("beforehide", onBeforeHide);
3531            menu.on("hide", onHide);
3532            menu.on("beforeshow", onBeforeShow);
3533            menu.on("show", onShow);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
3545          * Returns a {@link Roo.menu.Menu} object
3546          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547          * be used to generate and return a new Menu instance.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
3555            /*else if(typeof menu.length == 'number'){ // array of menu items?
3556                return new Roo.bootstrap.Menu({items:menu});
3557            }else{ // otherwise, must be a config
3558                return new Roo.bootstrap.Menu(menu);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
3565        unregister : function(menu){
3566            delete menus[menu.id];
3567            menu.un("beforehide", onBeforeHide);
3568            menu.un("hide", onHide);
3569            menu.un("beforeshow", onBeforeShow);
3570            menu.un("show", onShow);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.Menu
3608  * @extends Roo.bootstrap.Component
3609  * Bootstrap Menu class - container for MenuItems
3610  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3612  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3613  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3614   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3615   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
3625     if (config.type == 'treeview') {
3626         // normally menu's are drawn attached to the document to handle layering etc..
3627         // however treeview (used by the docs menu is drawn into the parent element)
3628         this.container_method = 'getChildContainer'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
3664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665          * @param {Roo.menu.Menu} this
3666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667          * @param {Roo.EventObject} e
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
3672          * Fires when the mouse is hovering over this menu
3673          * @param {Roo.menu.Menu} this
3674          * @param {Roo.EventObject} e
3675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
3680          * Fires when the mouse exits this menu
3681          * @param {Roo.menu.Menu} this
3682          * @param {Roo.EventObject} e
3683          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
3688          * Fires when a menu item contained in this menu is clicked
3689          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690          * @param {Roo.EventObject} e
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
3761             if (!this.hideTrigger) {
3762                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763                     // dropdown toggle on the 'a' in BS4?
3764                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
3815         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3816             if(t == this.activeItem && t.shouldDeactivate(e)){
3817                 this.activeItem.deactivate();
3818                 delete this.activeItem;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
3872      * Displays this menu relative to another element
3873      * @param {String/HTMLElement/Roo.Element} element The element to align to
3874      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875      * the element (defaults to this.defaultAlign)
3876      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
3895         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896             // if it goes to far to the right.. -> align left.
3897             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
3915      * Displays this menu at a specific xy position
3916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
3968      * Hides this menu and optionally all parent menus
3969      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
4017         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018         var pel = Roo.get(e.getTarget());
4019         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020             Roo.log('is treeview or dropdown?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
4087  * @class Roo.bootstrap.MenuItem
4088  * @extends Roo.bootstrap.Component
4089  * Bootstrap MenuItem class
4090  * @cfg {String} html the menu label
4091  * @cfg {String} href the link
4092  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4095  * @cfg {String} fa favicon to show on left of menu item.
4096  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
4253  * @class Roo.bootstrap.Modal
4254  * @extends Roo.bootstrap.Component
4255  * Bootstrap Modal class
4256  * @cfg {String} title Title of dialog
4257  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4259  * @cfg {Boolean} specificTitle default false
4260  * @cfg {Array} buttons Array of buttons or standard button set..
4261  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262  * @cfg {Boolean} animate default true
4263  * @cfg {Boolean} allow_close default true
4264  * @cfg {Boolean} fitwindow default false
4265  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268  * @cfg {String} size (sm|lg|xl) default empty
4269  * @cfg {Number} max_width set the max width of modal
4270  * @cfg {Boolean} editableTitle can the title be edited
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
4379         this.dialogEl = this.el.select('.modal-dialog',true).first();
4380         this.bodyEl = this.el.select('.modal-body',true).first();
4381         this.closeEl = this.el.select('.modal-header .close', true).first();
4382         this.headerEl = this.el.select('.modal-header',true).first();
4383         this.titleEl = this.el.select('.modal-title',true).first();
4384         this.footerEl = this.el.select('.modal-footer',true).first();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
4390         if (this.buttons.length) {
4391             Roo.each(this.buttons, function(bb) {
4392                 var b = Roo.apply({}, bb);
4393                 b.xns = b.xns || Roo.bootstrap;
4394                 b.xtype = b.xtype || 'Button';
4395                 if (typeof(b.listeners) == 'undefined') {
4396                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
4545         Roo.EventManager.onWindowResize(this.resize, this, true);
4546         if (this.editableTitle) {
4547             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4548             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549             this.headerEditEl.on('keyup', function(e) {
4550                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551                         this.toggleHeaderInput(false)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
4652         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4653         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654         this.maskEl.dom.style.display = 'block';
4655         this.maskEl.addClass('show');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
4679             this.maskEl.dom.style.display = '';
4680             Roo.get(document.body).removeClass("x-body-masked");
4681             this.el.removeClass('in');
4682             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
4718         var b = Roo.apply({}, { html : str } );
4719         b.xns = b.xns || Roo.bootstrap;
4720         b.xtype = b.xtype || 'Button';
4721         if (typeof(b.listeners) == 'undefined') {
4722             b.listeners = { click : cb.createDelegate(this)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
4774      * Set the body of the Dialog using the template
4775      * @param {Obj} data - apply this data to the template and replace the body contents.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
4864         // flip back to not editing.
4865         this.title = this.headerEditEl.dom.value;
4866         this.headerEditEl.addClass('d-none');
4867         this.titleEl.removeClass('d-none');
4868         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869         this.is_header_editing = false;
4870         this.fireEvent('titlechanged', this, this.title);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
4966    title:'Save Changes?',
4967    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968    buttons: Roo.Msg.YESNOCANCEL,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
5066                 this.buttons = buttons;
5067                 var bt = this.buttonText;
5068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
5075                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076                         '<textarea class="roo-mb-textarea"></textarea>' +
5077                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5078                 });
5079                 msgEl = bodyEl.dom.firstChild;
5080                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081                 textboxEl.enableDisplayMode();
5082                 textboxEl.addKeyListener([10,13], function(){
5083                     if(dlg.isVisible() && opt && opt.buttons){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
5108          * Updates the message box body text
5109          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110          * the XHTML-compliant non-breaking space character '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
5115             if(!dlg.isVisible() && !opt.width){
5116                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
5136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
5157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161          * @return {Roo.MessageBox} This message box
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
5168             if (pp) { // weird bug on my firefox - for some reason this is not defined
5169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
5193          * Displays a new message box, or reinitializes an existing message box, based on the config options
5194          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195          * The following config object properties are supported:
5196          * <pre>
5197 Property    Type             Description
5198 ----------  ---------------  ------------------------------------------------------------------------------------
5199 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5200                                    closes (defaults to undefined)
5201 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5204                                    progress and wait dialogs will ignore this property and always hide the
5205                                    close button as they can only be closed programmatically.
5206 cls               String           A custom CSS class to apply to the message box element
5207 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5208                                    displayed (defaults to 75)
5209 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5210                                    function will be btn (the name of the button that was clicked, if applicable,
5211                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5212                                    Progress and wait dialogs will ignore this option since they do not respond to
5213                                    user actions and can only be closed programmatically, so any required function
5214                                    should be called by the same code after it closes the dialog.
5215 icon              String           A CSS class that provides a background image to be used as an icon for
5216                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5218 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5219 modal             Boolean          False to allow user interaction with the page while the message box is
5220                                    displayed (defaults to true)
5221 msg               String           A string that will replace the existing message box body text (defaults
5222                                    to the XHTML-compliant non-breaking space character '&#160;')
5223 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5224 progress          Boolean          True to display a progress bar (defaults to false)
5225 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5228 title             String           The title text
5229 value             String           The string value to set into the active textbox element if displayed
5230 wait              Boolean          True to display a progress bar (defaults to false)
5231 width             Number           The width of the dialog in pixels
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
5258                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5260                 Roo.log("New Dialog Message:" +  options.msg )
5261                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
5313                 // force it to the end of the z-index stack so it gets a cursor in FF
5314                 document.body.appendChild(dlg.el.dom);
5315                 d.animateTarget = null;
5316                 d.show(options.animEl);
5317             }
5318             return this;
5319         },
5320
5321         /**
5322          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5323          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324          * and closing the message box when the process is complete.
5325          * @param {String} title The title bar text
5326          * @param {String} msg The message box body text
5327          * @return {Roo.MessageBox} This message box
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
5343          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344          * If a callback function is passed it will be called after the user clicks the button, and the
5345          * id of the button that was clicked will be passed as the only parameter to the callback
5346          * (could also be the top-right close button).
5347          * @param {String} title The title bar text
5348          * @param {String} msg The message box body text
5349          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350          * @param {Object} scope (optional) The scope of the callback function
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370          * You are responsible for closing the message box when the process is complete.
5371          * @param {String} msg The message box body text
5372          * @param {String} title (optional) The title bar text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399          * @param {String} title The title bar text
5400          * @param {String} msg The message box body text
5401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402          * @param {Object} scope (optional) The scope of the callback function
5403          * @return {Roo.MessageBox} This message box
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
5418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421          * (could also be the top-right close button) and the text that was entered will be passed as the two
5422          * parameters to the callback.
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429          * @return {Roo.MessageBox} This message box
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
5483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
5489          * An object containing the default button text strings that can be overriden for localized language support.
5490          * Supported properties are: ok, cancel, yes and no.
5491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
5634         ce.setHeight(0); // resize it ...
5635         ce.on('transitionend', function() {
5636             //Roo.log('done transition');
5637             ce.removeClass('collapsing');
5638             ce.addClass('show');
5639             ce.removeClass('collapse');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
5818  * @cfg {String} brand what is brand
5819  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820  * @cfg {String} brand_href href of the brand
5821  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5822  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
5920         if (this.brand !== '') {
5921             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
6044  * @class Roo.bootstrap.NavGroup
6045  * @extends Roo.bootstrap.Component
6046  * Bootstrap NavGroup class
6047  * @cfg {String} align (left|right)
6048  * @cfg {Boolean} inverse
6049  * @cfg {String} type (nav|pills|tab) default nav
6050  * @cfg {String} navId - reference Id for navbar.
6051  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
6066              * Fires when the active item changes
6067              * @param {Roo.bootstrap.NavGroup} this
6068              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
6100                 // trying to remove so header bar can right align top?
6101                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102                     // do not use on header bar... 
6103                     cfg.cls += ' navbar-nav';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
6277     clearWasActive : function(except) {
6278         Roo.each(this.navItems, function(e) {
6279             if (e.tabId != except.tabId && e.was_active) {
6280                e.was_active = false;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.NavItem
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap Navbar.NavItem class
6345  * @cfg {String} href  link to
6346  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347  * @cfg {Boolean} button_outline show and outlined button
6348  * @cfg {String} html content of button
6349  * @cfg {String} badge text inside badge
6350  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351  * @cfg {String} glyphicon DEPRICATED - use fa
6352  * @cfg {String} icon DEPRICATED - use fa
6353  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354  * @cfg {Boolean} active Is item active
6355  * @cfg {Boolean} disabled Is item disabled
6356  * @cfg {String} linkcls  Link Class
6357  * @cfg {Boolean} preventDefault (true | false) default false
6358  * @cfg {String} tabId the tab that this item activates.
6359  * @cfg {String} tagtype (a|span) render as a href or span?
6360  * @cfg {Boolean} animateRef (true|false) link to element default false  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
6435         if (this.button_weight.length) {
6436             cfg.tag = this.href ? 'a' : 'button';
6437             cfg.html = this.html || '';
6438             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
6471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472             } else  if (this.fa) {
6473                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474             } else if(this.glyphicon) {
6475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
6501         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502         this.navLink = this.el.select('.nav-link',true).first();
6503         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
6566         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569                 return; // ignore... - it's a 'hash' to another page.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
6585         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587             // remove the collapsed menu expand...
6588             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
6636         // if we can not flip to new panel - go back to old nav highlight..
6637         if (false == tg.showPanel(pan)) {
6638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
6727  * @class Roo.bootstrap.NavSidebarItem
6728  * @extends Roo.bootstrap.NavItem
6729  * Bootstrap Navbar.NavSidebarItem class
6730  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731  * {Boolean} open is the menu open
6732  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734  * {String} buttonSize (sm|md|lg)the extra classes for the button
6735  * {Boolean} showArrow show arrow next to the text (default true)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
6814         if (this.glyphicon || this.icon) {
6815             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6816             a.cn.push({ tag : 'i', cls : c }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
6847         if (typeof (this.menu) != 'undefined') {
6848             this.menu.parentType = this.xtype;
6849             this.menu.triggerEl = this.el;
6850             this.menu = this.addxtype(Roo.apply({}, this.menu));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
7012  * @children Roo.bootstrap.breadcrumb.Component
7013  * @cfg {String} html the content of the link.
7014  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015  * @cfg {Boolean} active is it active
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.Pagination
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap Pagination class
7122  * @cfg {String} size xs | sm | md | lg
7123  * @cfg {Boolean} inverse false | true
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
7170  * @class Roo.bootstrap.PaginationItem
7171  * @extends Roo.bootstrap.Component
7172  * Bootstrap PaginationItem class
7173  * @cfg {String} html text
7174  * @cfg {String} href the link
7175  * @cfg {Boolean} preventDefault (true | false) default true
7176  * @cfg {Boolean} active (true | false) default false
7177  * @cfg {Boolean} disabled default false
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  /**
7311  * @extends Roo.dd.DDProxy
7312  * @class Roo.grid.SplitDragZone
7313  * Support for Column Header resizing
7314  * @constructor
7315  * @param {Object} config
7316  */
7317 // private
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7320     this.grid = grid;
7321     this.view = grid.getView();
7322     this.proxy = this.view.resizeProxy;
7323     Roo.grid.SplitDragZone.superclass.constructor.call(
7324         this,
7325         hd, // ID
7326         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7327         {  // CONFIG
7328             dragElId : Roo.id(this.proxy.dom),
7329             resizeFrame:false
7330         }
7331     );
7332     
7333     this.setHandleElId(Roo.id(hd));
7334     if (hd2 !== false) {
7335         this.setOuterHandleElId(Roo.id(hd2));
7336     }
7337     
7338     this.scroll = false;
7339 };
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341     fly: Roo.Element.fly,
7342
7343     b4StartDrag : function(x, y){
7344         this.view.headersDisabled = true;
7345         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7347         );
7348         this.proxy.setHeight(h);
7349         
7350         // for old system colWidth really stored the actual width?
7351         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352         // which in reality did not work.. - it worked only for fixed sizes
7353         // for resizable we need to use actual sizes.
7354         var w = this.cm.getColumnWidth(this.cellIndex);
7355         if (!this.view.mainWrap) {
7356             // bootstrap.
7357             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7358         }
7359         
7360         
7361         
7362         // this was w-this.grid.minColumnWidth;
7363         // doesnt really make sense? - w = thie curren width or the rendered one?
7364         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365         this.resetConstraints();
7366         this.setXConstraint(minw, 1000);
7367         this.setYConstraint(0, 0);
7368         this.minX = x - minw;
7369         this.maxX = x + 1000;
7370         this.startPos = x;
7371         if (!this.view.mainWrap) { // this is Bootstrap code..
7372             this.getDragEl().style.display='block';
7373         }
7374         
7375         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7376     },
7377
7378
7379     handleMouseDown : function(e){
7380         ev = Roo.EventObject.setEvent(e);
7381         var t = this.fly(ev.getTarget());
7382         if(t.hasClass("x-grid-split")){
7383             this.cellIndex = this.view.getCellIndex(t.dom);
7384             this.split = t.dom;
7385             this.cm = this.grid.colModel;
7386             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7388             }
7389         }
7390     },
7391
7392     endDrag : function(e){
7393         this.view.headersDisabled = false;
7394         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395         var diff = endX - this.startPos;
7396         // 
7397         var w = this.cm.getColumnWidth(this.cellIndex);
7398         if (!this.view.mainWrap) {
7399             w = 0;
7400         }
7401         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7402     },
7403
7404     autoOffset : function(){
7405         this.setDelta(0,0);
7406     }
7407 });/*
7408  * Based on:
7409  * Ext JS Library 1.1.1
7410  * Copyright(c) 2006-2007, Ext JS, LLC.
7411  *
7412  * Originally Released Under LGPL - original licence link has changed is not relivant.
7413  *
7414  * Fork - LGPL
7415  * <script type="text/javascript">
7416  */
7417
7418 /**
7419  * @class Roo.grid.AbstractSelectionModel
7420  * @extends Roo.util.Observable
7421  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7422  * implemented by descendant classes.  This class should not be directly instantiated.
7423  * @constructor
7424  */
7425 Roo.grid.AbstractSelectionModel = function(){
7426     this.locked = false;
7427     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7428 };
7429
7430 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7431     /** @ignore Called by the grid automatically. Do not call directly. */
7432     init : function(grid){
7433         this.grid = grid;
7434         this.initEvents();
7435     },
7436
7437     /**
7438      * Locks the selections.
7439      */
7440     lock : function(){
7441         this.locked = true;
7442     },
7443
7444     /**
7445      * Unlocks the selections.
7446      */
7447     unlock : function(){
7448         this.locked = false;
7449     },
7450
7451     /**
7452      * Returns true if the selections are locked.
7453      * @return {Boolean}
7454      */
7455     isLocked : function(){
7456         return this.locked;
7457     }
7458 });/*
7459  * Based on:
7460  * Ext JS Library 1.1.1
7461  * Copyright(c) 2006-2007, Ext JS, LLC.
7462  *
7463  * Originally Released Under LGPL - original licence link has changed is not relivant.
7464  *
7465  * Fork - LGPL
7466  * <script type="text/javascript">
7467  */
7468 /**
7469  * @extends Roo.grid.AbstractSelectionModel
7470  * @class Roo.grid.RowSelectionModel
7471  * The default SelectionModel used by {@link Roo.grid.Grid}.
7472  * It supports multiple selections and keyboard selection/navigation. 
7473  * @constructor
7474  * @param {Object} config
7475  */
7476 Roo.grid.RowSelectionModel = function(config){
7477     Roo.apply(this, config);
7478     this.selections = new Roo.util.MixedCollection(false, function(o){
7479         return o.id;
7480     });
7481
7482     this.last = false;
7483     this.lastActive = false;
7484
7485     this.addEvents({
7486         /**
7487         * @event selectionchange
7488         * Fires when the selection changes
7489         * @param {SelectionModel} this
7490         */
7491        "selectionchange" : true,
7492        /**
7493         * @event afterselectionchange
7494         * Fires after the selection changes (eg. by key press or clicking)
7495         * @param {SelectionModel} this
7496         */
7497        "afterselectionchange" : true,
7498        /**
7499         * @event beforerowselect
7500         * Fires when a row is selected being selected, return false to cancel.
7501         * @param {SelectionModel} this
7502         * @param {Number} rowIndex The selected index
7503         * @param {Boolean} keepExisting False if other selections will be cleared
7504         */
7505        "beforerowselect" : true,
7506        /**
7507         * @event rowselect
7508         * Fires when a row is selected.
7509         * @param {SelectionModel} this
7510         * @param {Number} rowIndex The selected index
7511         * @param {Roo.data.Record} r The record
7512         */
7513        "rowselect" : true,
7514        /**
7515         * @event rowdeselect
7516         * Fires when a row is deselected.
7517         * @param {SelectionModel} this
7518         * @param {Number} rowIndex The selected index
7519         */
7520         "rowdeselect" : true
7521     });
7522     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7523     this.locked = false;
7524 };
7525
7526 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7527     /**
7528      * @cfg {Boolean} singleSelect
7529      * True to allow selection of only one row at a time (defaults to false)
7530      */
7531     singleSelect : false,
7532
7533     // private
7534     initEvents : function(){
7535
7536         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7537             this.grid.on("mousedown", this.handleMouseDown, this);
7538         }else{ // allow click to work like normal
7539             this.grid.on("rowclick", this.handleDragableRowClick, this);
7540         }
7541         // bootstrap does not have a view..
7542         var view = this.grid.view ? this.grid.view : this.grid;
7543         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7544             "up" : function(e){
7545                 if(!e.shiftKey){
7546                     this.selectPrevious(e.shiftKey);
7547                 }else if(this.last !== false && this.lastActive !== false){
7548                     var last = this.last;
7549                     this.selectRange(this.last,  this.lastActive-1);
7550                     view.focusRow(this.lastActive);
7551                     if(last !== false){
7552                         this.last = last;
7553                     }
7554                 }else{
7555                     this.selectFirstRow();
7556                 }
7557                 this.fireEvent("afterselectionchange", this);
7558             },
7559             "down" : function(e){
7560                 if(!e.shiftKey){
7561                     this.selectNext(e.shiftKey);
7562                 }else if(this.last !== false && this.lastActive !== false){
7563                     var last = this.last;
7564                     this.selectRange(this.last,  this.lastActive+1);
7565                     view.focusRow(this.lastActive);
7566                     if(last !== false){
7567                         this.last = last;
7568                     }
7569                 }else{
7570                     this.selectFirstRow();
7571                 }
7572                 this.fireEvent("afterselectionchange", this);
7573             },
7574             scope: this
7575         });
7576
7577          
7578         view.on("refresh", this.onRefresh, this);
7579         view.on("rowupdated", this.onRowUpdated, this);
7580         view.on("rowremoved", this.onRemove, this);
7581     },
7582
7583     // private
7584     onRefresh : function(){
7585         var ds = this.grid.ds, i, v = this.grid.view;
7586         var s = this.selections;
7587         s.each(function(r){
7588             if((i = ds.indexOfId(r.id)) != -1){
7589                 v.onRowSelect(i);
7590                 s.add(ds.getAt(i)); // updating the selection relate data
7591             }else{
7592                 s.remove(r);
7593             }
7594         });
7595     },
7596
7597     // private
7598     onRemove : function(v, index, r){
7599         this.selections.remove(r);
7600     },
7601
7602     // private
7603     onRowUpdated : function(v, index, r){
7604         if(this.isSelected(r)){
7605             v.onRowSelect(index);
7606         }
7607     },
7608
7609     /**
7610      * Select records.
7611      * @param {Array} records The records to select
7612      * @param {Boolean} keepExisting (optional) True to keep existing selections
7613      */
7614     selectRecords : function(records, keepExisting){
7615         if(!keepExisting){
7616             this.clearSelections();
7617         }
7618         var ds = this.grid.ds;
7619         for(var i = 0, len = records.length; i < len; i++){
7620             this.selectRow(ds.indexOf(records[i]), true);
7621         }
7622     },
7623
7624     /**
7625      * Gets the number of selected rows.
7626      * @return {Number}
7627      */
7628     getCount : function(){
7629         return this.selections.length;
7630     },
7631
7632     /**
7633      * Selects the first row in the grid.
7634      */
7635     selectFirstRow : function(){
7636         this.selectRow(0);
7637     },
7638
7639     /**
7640      * Select the last row.
7641      * @param {Boolean} keepExisting (optional) True to keep existing selections
7642      */
7643     selectLastRow : function(keepExisting){
7644         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7645     },
7646
7647     /**
7648      * Selects the row immediately following the last selected row.
7649      * @param {Boolean} keepExisting (optional) True to keep existing selections
7650      */
7651     selectNext : function(keepExisting){
7652         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7653             this.selectRow(this.last+1, keepExisting);
7654             var view = this.grid.view ? this.grid.view : this.grid;
7655             view.focusRow(this.last);
7656         }
7657     },
7658
7659     /**
7660      * Selects the row that precedes the last selected row.
7661      * @param {Boolean} keepExisting (optional) True to keep existing selections
7662      */
7663     selectPrevious : function(keepExisting){
7664         if(this.last){
7665             this.selectRow(this.last-1, keepExisting);
7666             var view = this.grid.view ? this.grid.view : this.grid;
7667             view.focusRow(this.last);
7668         }
7669     },
7670
7671     /**
7672      * Returns the selected records
7673      * @return {Array} Array of selected records
7674      */
7675     getSelections : function(){
7676         return [].concat(this.selections.items);
7677     },
7678
7679     /**
7680      * Returns the first selected record.
7681      * @return {Record}
7682      */
7683     getSelected : function(){
7684         return this.selections.itemAt(0);
7685     },
7686
7687
7688     /**
7689      * Clears all selections.
7690      */
7691     clearSelections : function(fast){
7692         if(this.locked) {
7693             return;
7694         }
7695         if(fast !== true){
7696             var ds = this.grid.ds;
7697             var s = this.selections;
7698             s.each(function(r){
7699                 this.deselectRow(ds.indexOfId(r.id));
7700             }, this);
7701             s.clear();
7702         }else{
7703             this.selections.clear();
7704         }
7705         this.last = false;
7706     },
7707
7708
7709     /**
7710      * Selects all rows.
7711      */
7712     selectAll : function(){
7713         if(this.locked) {
7714             return;
7715         }
7716         this.selections.clear();
7717         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7718             this.selectRow(i, true);
7719         }
7720     },
7721
7722     /**
7723      * Returns True if there is a selection.
7724      * @return {Boolean}
7725      */
7726     hasSelection : function(){
7727         return this.selections.length > 0;
7728     },
7729
7730     /**
7731      * Returns True if the specified row is selected.
7732      * @param {Number/Record} record The record or index of the record to check
7733      * @return {Boolean}
7734      */
7735     isSelected : function(index){
7736         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7737         return (r && this.selections.key(r.id) ? true : false);
7738     },
7739
7740     /**
7741      * Returns True if the specified record id is selected.
7742      * @param {String} id The id of record to check
7743      * @return {Boolean}
7744      */
7745     isIdSelected : function(id){
7746         return (this.selections.key(id) ? true : false);
7747     },
7748
7749     // private
7750     handleMouseDown : function(e, t)
7751     {
7752         var view = this.grid.view ? this.grid.view : this.grid;
7753         var rowIndex;
7754         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7755             return;
7756         };
7757         if(e.shiftKey && this.last !== false){
7758             var last = this.last;
7759             this.selectRange(last, rowIndex, e.ctrlKey);
7760             this.last = last; // reset the last
7761             view.focusRow(rowIndex);
7762         }else{
7763             var isSelected = this.isSelected(rowIndex);
7764             if(e.button !== 0 && isSelected){
7765                 view.focusRow(rowIndex);
7766             }else if(e.ctrlKey && isSelected){
7767                 this.deselectRow(rowIndex);
7768             }else if(!isSelected){
7769                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7770                 view.focusRow(rowIndex);
7771             }
7772         }
7773         this.fireEvent("afterselectionchange", this);
7774     },
7775     // private
7776     handleDragableRowClick :  function(grid, rowIndex, e) 
7777     {
7778         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7779             this.selectRow(rowIndex, false);
7780             var view = this.grid.view ? this.grid.view : this.grid;
7781             view.focusRow(rowIndex);
7782              this.fireEvent("afterselectionchange", this);
7783         }
7784     },
7785     
7786     /**
7787      * Selects multiple rows.
7788      * @param {Array} rows Array of the indexes of the row to select
7789      * @param {Boolean} keepExisting (optional) True to keep existing selections
7790      */
7791     selectRows : function(rows, keepExisting){
7792         if(!keepExisting){
7793             this.clearSelections();
7794         }
7795         for(var i = 0, len = rows.length; i < len; i++){
7796             this.selectRow(rows[i], true);
7797         }
7798     },
7799
7800     /**
7801      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7802      * @param {Number} startRow The index of the first row in the range
7803      * @param {Number} endRow The index of the last row in the range
7804      * @param {Boolean} keepExisting (optional) True to retain existing selections
7805      */
7806     selectRange : function(startRow, endRow, keepExisting){
7807         if(this.locked) {
7808             return;
7809         }
7810         if(!keepExisting){
7811             this.clearSelections();
7812         }
7813         if(startRow <= endRow){
7814             for(var i = startRow; i <= endRow; i++){
7815                 this.selectRow(i, true);
7816             }
7817         }else{
7818             for(var i = startRow; i >= endRow; i--){
7819                 this.selectRow(i, true);
7820             }
7821         }
7822     },
7823
7824     /**
7825      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7826      * @param {Number} startRow The index of the first row in the range
7827      * @param {Number} endRow The index of the last row in the range
7828      */
7829     deselectRange : function(startRow, endRow, preventViewNotify){
7830         if(this.locked) {
7831             return;
7832         }
7833         for(var i = startRow; i <= endRow; i++){
7834             this.deselectRow(i, preventViewNotify);
7835         }
7836     },
7837
7838     /**
7839      * Selects a row.
7840      * @param {Number} row The index of the row to select
7841      * @param {Boolean} keepExisting (optional) True to keep existing selections
7842      */
7843     selectRow : function(index, keepExisting, preventViewNotify){
7844         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7845             return;
7846         }
7847         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7848             if(!keepExisting || this.singleSelect){
7849                 this.clearSelections();
7850             }
7851             var r = this.grid.ds.getAt(index);
7852             this.selections.add(r);
7853             this.last = this.lastActive = index;
7854             if(!preventViewNotify){
7855                 var view = this.grid.view ? this.grid.view : this.grid;
7856                 view.onRowSelect(index);
7857             }
7858             this.fireEvent("rowselect", this, index, r);
7859             this.fireEvent("selectionchange", this);
7860         }
7861     },
7862
7863     /**
7864      * Deselects a row.
7865      * @param {Number} row The index of the row to deselect
7866      */
7867     deselectRow : function(index, preventViewNotify){
7868         if(this.locked) {
7869             return;
7870         }
7871         if(this.last == index){
7872             this.last = false;
7873         }
7874         if(this.lastActive == index){
7875             this.lastActive = false;
7876         }
7877         var r = this.grid.ds.getAt(index);
7878         this.selections.remove(r);
7879         if(!preventViewNotify){
7880             var view = this.grid.view ? this.grid.view : this.grid;
7881             view.onRowDeselect(index);
7882         }
7883         this.fireEvent("rowdeselect", this, index);
7884         this.fireEvent("selectionchange", this);
7885     },
7886
7887     // private
7888     restoreLast : function(){
7889         if(this._last){
7890             this.last = this._last;
7891         }
7892     },
7893
7894     // private
7895     acceptsNav : function(row, col, cm){
7896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7897     },
7898
7899     // private
7900     onEditorKey : function(field, e){
7901         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7902         if(k == e.TAB){
7903             e.stopEvent();
7904             ed.completeEdit();
7905             if(e.shiftKey){
7906                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7907             }else{
7908                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7909             }
7910         }else if(k == e.ENTER && !e.ctrlKey){
7911             e.stopEvent();
7912             ed.completeEdit();
7913             if(e.shiftKey){
7914                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7915             }else{
7916                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7917             }
7918         }else if(k == e.ESC){
7919             ed.cancelEdit();
7920         }
7921         if(newCell){
7922             g.startEditing(newCell[0], newCell[1]);
7923         }
7924     }
7925 });/*
7926  * Based on:
7927  * Ext JS Library 1.1.1
7928  * Copyright(c) 2006-2007, Ext JS, LLC.
7929  *
7930  * Originally Released Under LGPL - original licence link has changed is not relivant.
7931  *
7932  * Fork - LGPL
7933  * <script type="text/javascript">
7934  */
7935  
7936
7937 /**
7938  * @class Roo.grid.ColumnModel
7939  * @extends Roo.util.Observable
7940  * This is the default implementation of a ColumnModel used by the Grid. It defines
7941  * the columns in the grid.
7942  * <br>Usage:<br>
7943  <pre><code>
7944  var colModel = new Roo.grid.ColumnModel([
7945         {header: "Ticker", width: 60, sortable: true, locked: true},
7946         {header: "Company Name", width: 150, sortable: true},
7947         {header: "Market Cap.", width: 100, sortable: true},
7948         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7949         {header: "Employees", width: 100, sortable: true, resizable: false}
7950  ]);
7951  </code></pre>
7952  * <p>
7953  
7954  * The config options listed for this class are options which may appear in each
7955  * individual column definition.
7956  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7957  * @constructor
7958  * @param {Object} config An Array of column config objects. See this class's
7959  * config objects for details.
7960 */
7961 Roo.grid.ColumnModel = function(config){
7962         /**
7963      * The config passed into the constructor
7964      */
7965     this.config = []; //config;
7966     this.lookup = {};
7967
7968     // if no id, create one
7969     // if the column does not have a dataIndex mapping,
7970     // map it to the order it is in the config
7971     for(var i = 0, len = config.length; i < len; i++){
7972         this.addColumn(config[i]);
7973         
7974     }
7975
7976     /**
7977      * The width of columns which have no width specified (defaults to 100)
7978      * @type Number
7979      */
7980     this.defaultWidth = 100;
7981
7982     /**
7983      * Default sortable of columns which have no sortable specified (defaults to false)
7984      * @type Boolean
7985      */
7986     this.defaultSortable = false;
7987
7988     this.addEvents({
7989         /**
7990              * @event widthchange
7991              * Fires when the width of a column changes.
7992              * @param {ColumnModel} this
7993              * @param {Number} columnIndex The column index
7994              * @param {Number} newWidth The new width
7995              */
7996             "widthchange": true,
7997         /**
7998              * @event headerchange
7999              * Fires when the text of a header changes.
8000              * @param {ColumnModel} this
8001              * @param {Number} columnIndex The column index
8002              * @param {Number} newText The new header text
8003              */
8004             "headerchange": true,
8005         /**
8006              * @event hiddenchange
8007              * Fires when a column is hidden or "unhidden".
8008              * @param {ColumnModel} this
8009              * @param {Number} columnIndex The column index
8010              * @param {Boolean} hidden true if hidden, false otherwise
8011              */
8012             "hiddenchange": true,
8013             /**
8014          * @event columnmoved
8015          * Fires when a column is moved.
8016          * @param {ColumnModel} this
8017          * @param {Number} oldIndex
8018          * @param {Number} newIndex
8019          */
8020         "columnmoved" : true,
8021         /**
8022          * @event columlockchange
8023          * Fires when a column's locked state is changed
8024          * @param {ColumnModel} this
8025          * @param {Number} colIndex
8026          * @param {Boolean} locked true if locked
8027          */
8028         "columnlockchange" : true
8029     });
8030     Roo.grid.ColumnModel.superclass.constructor.call(this);
8031 };
8032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8033     /**
8034      * @cfg {String} header The header text to display in the Grid view.
8035      */
8036         /**
8037      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8038      */
8039         /**
8040      * @cfg {String} smHeader Header at Bootsrap Small width
8041      */
8042         /**
8043      * @cfg {String} mdHeader Header at Bootsrap Medium width
8044      */
8045         /**
8046      * @cfg {String} lgHeader Header at Bootsrap Large width
8047      */
8048         /**
8049      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8050      */
8051     /**
8052      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8053      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8054      * specified, the column's index is used as an index into the Record's data Array.
8055      */
8056     /**
8057      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8058      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8059      */
8060     /**
8061      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8062      * Defaults to the value of the {@link #defaultSortable} property.
8063      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8064      */
8065     /**
8066      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8067      */
8068     /**
8069      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8070      */
8071     /**
8072      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8073      */
8074     /**
8075      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8076      */
8077     /**
8078      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8079      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8080      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8081      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8082      */
8083        /**
8084      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8085      */
8086     /**
8087      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8088      */
8089     /**
8090      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8091      */
8092     /**
8093      * @cfg {String} cursor (Optional)
8094      */
8095     /**
8096      * @cfg {String} tooltip (Optional)
8097      */
8098     /**
8099      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8100      */
8101     /**
8102      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8103      */
8104     /**
8105      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8106      */
8107     /**
8108      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8109      */
8110         /**
8111      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8112      */
8113     /**
8114      * Returns the id of the column at the specified index.
8115      * @param {Number} index The column index
8116      * @return {String} the id
8117      */
8118     getColumnId : function(index){
8119         return this.config[index].id;
8120     },
8121
8122     /**
8123      * Returns the column for a specified id.
8124      * @param {String} id The column id
8125      * @return {Object} the column
8126      */
8127     getColumnById : function(id){
8128         return this.lookup[id];
8129     },
8130
8131     
8132     /**
8133      * Returns the column Object for a specified dataIndex.
8134      * @param {String} dataIndex The column dataIndex
8135      * @return {Object|Boolean} the column or false if not found
8136      */
8137     getColumnByDataIndex: function(dataIndex){
8138         var index = this.findColumnIndex(dataIndex);
8139         return index > -1 ? this.config[index] : false;
8140     },
8141     
8142     /**
8143      * Returns the index for a specified column id.
8144      * @param {String} id The column id
8145      * @return {Number} the index, or -1 if not found
8146      */
8147     getIndexById : function(id){
8148         for(var i = 0, len = this.config.length; i < len; i++){
8149             if(this.config[i].id == id){
8150                 return i;
8151             }
8152         }
8153         return -1;
8154     },
8155     
8156     /**
8157      * Returns the index for a specified column dataIndex.
8158      * @param {String} dataIndex The column dataIndex
8159      * @return {Number} the index, or -1 if not found
8160      */
8161     
8162     findColumnIndex : function(dataIndex){
8163         for(var i = 0, len = this.config.length; i < len; i++){
8164             if(this.config[i].dataIndex == dataIndex){
8165                 return i;
8166             }
8167         }
8168         return -1;
8169     },
8170     
8171     
8172     moveColumn : function(oldIndex, newIndex){
8173         var c = this.config[oldIndex];
8174         this.config.splice(oldIndex, 1);
8175         this.config.splice(newIndex, 0, c);
8176         this.dataMap = null;
8177         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8178     },
8179
8180     isLocked : function(colIndex){
8181         return this.config[colIndex].locked === true;
8182     },
8183
8184     setLocked : function(colIndex, value, suppressEvent){
8185         if(this.isLocked(colIndex) == value){
8186             return;
8187         }
8188         this.config[colIndex].locked = value;
8189         if(!suppressEvent){
8190             this.fireEvent("columnlockchange", this, colIndex, value);
8191         }
8192     },
8193
8194     getTotalLockedWidth : function(){
8195         var totalWidth = 0;
8196         for(var i = 0; i < this.config.length; i++){
8197             if(this.isLocked(i) && !this.isHidden(i)){
8198                 this.totalWidth += this.getColumnWidth(i);
8199             }
8200         }
8201         return totalWidth;
8202     },
8203
8204     getLockedCount : function(){
8205         for(var i = 0, len = this.config.length; i < len; i++){
8206             if(!this.isLocked(i)){
8207                 return i;
8208             }
8209         }
8210         
8211         return this.config.length;
8212     },
8213
8214     /**
8215      * Returns the number of columns.
8216      * @return {Number}
8217      */
8218     getColumnCount : function(visibleOnly){
8219         if(visibleOnly === true){
8220             var c = 0;
8221             for(var i = 0, len = this.config.length; i < len; i++){
8222                 if(!this.isHidden(i)){
8223                     c++;
8224                 }
8225             }
8226             return c;
8227         }
8228         return this.config.length;
8229     },
8230
8231     /**
8232      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8233      * @param {Function} fn
8234      * @param {Object} scope (optional)
8235      * @return {Array} result
8236      */
8237     getColumnsBy : function(fn, scope){
8238         var r = [];
8239         for(var i = 0, len = this.config.length; i < len; i++){
8240             var c = this.config[i];
8241             if(fn.call(scope||this, c, i) === true){
8242                 r[r.length] = c;
8243             }
8244         }
8245         return r;
8246     },
8247
8248     /**
8249      * Returns true if the specified column is sortable.
8250      * @param {Number} col The column index
8251      * @return {Boolean}
8252      */
8253     isSortable : function(col){
8254         if(typeof this.config[col].sortable == "undefined"){
8255             return this.defaultSortable;
8256         }
8257         return this.config[col].sortable;
8258     },
8259
8260     /**
8261      * Returns the rendering (formatting) function defined for the column.
8262      * @param {Number} col The column index.
8263      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8264      */
8265     getRenderer : function(col){
8266         if(!this.config[col].renderer){
8267             return Roo.grid.ColumnModel.defaultRenderer;
8268         }
8269         return this.config[col].renderer;
8270     },
8271
8272     /**
8273      * Sets the rendering (formatting) function for a column.
8274      * @param {Number} col The column index
8275      * @param {Function} fn The function to use to process the cell's raw data
8276      * to return HTML markup for the grid view. The render function is called with
8277      * the following parameters:<ul>
8278      * <li>Data value.</li>
8279      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8280      * <li>css A CSS style string to apply to the table cell.</li>
8281      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8282      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8283      * <li>Row index</li>
8284      * <li>Column index</li>
8285      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8286      */
8287     setRenderer : function(col, fn){
8288         this.config[col].renderer = fn;
8289     },
8290
8291     /**
8292      * Returns the width for the specified column.
8293      * @param {Number} col The column index
8294      * @param (optional) {String} gridSize bootstrap width size.
8295      * @return {Number}
8296      */
8297     getColumnWidth : function(col, gridSize)
8298         {
8299                 var cfg = this.config[col];
8300                 
8301                 if (typeof(gridSize) == 'undefined') {
8302                         return cfg.width * 1 || this.defaultWidth;
8303                 }
8304                 if (gridSize === false) { // if we set it..
8305                         return cfg.width || false;
8306                 }
8307                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8308                 
8309                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8310                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8311                                 continue;
8312                         }
8313                         return cfg[ sizes[i] ];
8314                 }
8315                 return 1;
8316                 
8317     },
8318
8319     /**
8320      * Sets the width for a column.
8321      * @param {Number} col The column index
8322      * @param {Number} width The new width
8323      */
8324     setColumnWidth : function(col, width, suppressEvent){
8325         this.config[col].width = width;
8326         this.totalWidth = null;
8327         if(!suppressEvent){
8328              this.fireEvent("widthchange", this, col, width);
8329         }
8330     },
8331
8332     /**
8333      * Returns the total width of all columns.
8334      * @param {Boolean} includeHidden True to include hidden column widths
8335      * @return {Number}
8336      */
8337     getTotalWidth : function(includeHidden){
8338         if(!this.totalWidth){
8339             this.totalWidth = 0;
8340             for(var i = 0, len = this.config.length; i < len; i++){
8341                 if(includeHidden || !this.isHidden(i)){
8342                     this.totalWidth += this.getColumnWidth(i);
8343                 }
8344             }
8345         }
8346         return this.totalWidth;
8347     },
8348
8349     /**
8350      * Returns the header for the specified column.
8351      * @param {Number} col The column index
8352      * @return {String}
8353      */
8354     getColumnHeader : function(col){
8355         return this.config[col].header;
8356     },
8357
8358     /**
8359      * Sets the header for a column.
8360      * @param {Number} col The column index
8361      * @param {String} header The new header
8362      */
8363     setColumnHeader : function(col, header){
8364         this.config[col].header = header;
8365         this.fireEvent("headerchange", this, col, header);
8366     },
8367
8368     /**
8369      * Returns the tooltip for the specified column.
8370      * @param {Number} col The column index
8371      * @return {String}
8372      */
8373     getColumnTooltip : function(col){
8374             return this.config[col].tooltip;
8375     },
8376     /**
8377      * Sets the tooltip for a column.
8378      * @param {Number} col The column index
8379      * @param {String} tooltip The new tooltip
8380      */
8381     setColumnTooltip : function(col, tooltip){
8382             this.config[col].tooltip = tooltip;
8383     },
8384
8385     /**
8386      * Returns the dataIndex for the specified column.
8387      * @param {Number} col The column index
8388      * @return {Number}
8389      */
8390     getDataIndex : function(col){
8391         return this.config[col].dataIndex;
8392     },
8393
8394     /**
8395      * Sets the dataIndex for a column.
8396      * @param {Number} col The column index
8397      * @param {Number} dataIndex The new dataIndex
8398      */
8399     setDataIndex : function(col, dataIndex){
8400         this.config[col].dataIndex = dataIndex;
8401     },
8402
8403     
8404     
8405     /**
8406      * Returns true if the cell is editable.
8407      * @param {Number} colIndex The column index
8408      * @param {Number} rowIndex The row index - this is nto actually used..?
8409      * @return {Boolean}
8410      */
8411     isCellEditable : function(colIndex, rowIndex){
8412         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8413     },
8414
8415     /**
8416      * Returns the editor defined for the cell/column.
8417      * return false or null to disable editing.
8418      * @param {Number} colIndex The column index
8419      * @param {Number} rowIndex The row index
8420      * @return {Object}
8421      */
8422     getCellEditor : function(colIndex, rowIndex){
8423         return this.config[colIndex].editor;
8424     },
8425
8426     /**
8427      * Sets if a column is editable.
8428      * @param {Number} col The column index
8429      * @param {Boolean} editable True if the column is editable
8430      */
8431     setEditable : function(col, editable){
8432         this.config[col].editable = editable;
8433     },
8434
8435
8436     /**
8437      * Returns true if the column is hidden.
8438      * @param {Number} colIndex The column index
8439      * @return {Boolean}
8440      */
8441     isHidden : function(colIndex){
8442         return this.config[colIndex].hidden;
8443     },
8444
8445
8446     /**
8447      * Returns true if the column width cannot be changed
8448      */
8449     isFixed : function(colIndex){
8450         return this.config[colIndex].fixed;
8451     },
8452
8453     /**
8454      * Returns true if the column can be resized
8455      * @return {Boolean}
8456      */
8457     isResizable : function(colIndex){
8458         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8459     },
8460     /**
8461      * Sets if a column is hidden.
8462      * @param {Number} colIndex The column index
8463      * @param {Boolean} hidden True if the column is hidden
8464      */
8465     setHidden : function(colIndex, hidden){
8466         this.config[colIndex].hidden = hidden;
8467         this.totalWidth = null;
8468         this.fireEvent("hiddenchange", this, colIndex, hidden);
8469     },
8470
8471     /**
8472      * Sets the editor for a column.
8473      * @param {Number} col The column index
8474      * @param {Object} editor The editor object
8475      */
8476     setEditor : function(col, editor){
8477         this.config[col].editor = editor;
8478     },
8479     /**
8480      * Add a column (experimental...) - defaults to adding to the end..
8481      * @param {Object} config 
8482     */
8483     addColumn : function(c)
8484     {
8485     
8486         var i = this.config.length;
8487         this.config[i] = c;
8488         
8489         if(typeof c.dataIndex == "undefined"){
8490             c.dataIndex = i;
8491         }
8492         if(typeof c.renderer == "string"){
8493             c.renderer = Roo.util.Format[c.renderer];
8494         }
8495         if(typeof c.id == "undefined"){
8496             c.id = Roo.id();
8497         }
8498         if(c.editor && c.editor.xtype){
8499             c.editor  = Roo.factory(c.editor, Roo.grid);
8500         }
8501         if(c.editor && c.editor.isFormField){
8502             c.editor = new Roo.grid.GridEditor(c.editor);
8503         }
8504         this.lookup[c.id] = c;
8505     }
8506     
8507 });
8508
8509 Roo.grid.ColumnModel.defaultRenderer = function(value)
8510 {
8511     if(typeof value == "object") {
8512         return value;
8513     }
8514         if(typeof value == "string" && value.length < 1){
8515             return "&#160;";
8516         }
8517     
8518         return String.format("{0}", value);
8519 };
8520
8521 // Alias for backwards compatibility
8522 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8523 /*
8524  * Based on:
8525  * Ext JS Library 1.1.1
8526  * Copyright(c) 2006-2007, Ext JS, LLC.
8527  *
8528  * Originally Released Under LGPL - original licence link has changed is not relivant.
8529  *
8530  * Fork - LGPL
8531  * <script type="text/javascript">
8532  */
8533  
8534 /**
8535  * @class Roo.LoadMask
8536  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8537  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8538  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8539  * element's UpdateManager load indicator and will be destroyed after the initial load.
8540  * @constructor
8541  * Create a new LoadMask
8542  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8543  * @param {Object} config The config object
8544  */
8545 Roo.LoadMask = function(el, config){
8546     this.el = Roo.get(el);
8547     Roo.apply(this, config);
8548     if(this.store){
8549         this.store.on('beforeload', this.onBeforeLoad, this);
8550         this.store.on('load', this.onLoad, this);
8551         this.store.on('loadexception', this.onLoadException, this);
8552         this.removeMask = false;
8553     }else{
8554         var um = this.el.getUpdateManager();
8555         um.showLoadIndicator = false; // disable the default indicator
8556         um.on('beforeupdate', this.onBeforeLoad, this);
8557         um.on('update', this.onLoad, this);
8558         um.on('failure', this.onLoad, this);
8559         this.removeMask = true;
8560     }
8561 };
8562
8563 Roo.LoadMask.prototype = {
8564     /**
8565      * @cfg {Boolean} removeMask
8566      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8567      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8568      */
8569     /**
8570      * @cfg {String} msg
8571      * The text to display in a centered loading message box (defaults to 'Loading...')
8572      */
8573     msg : 'Loading...',
8574     /**
8575      * @cfg {String} msgCls
8576      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8577      */
8578     msgCls : 'x-mask-loading',
8579
8580     /**
8581      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8582      * @type Boolean
8583      */
8584     disabled: false,
8585
8586     /**
8587      * Disables the mask to prevent it from being displayed
8588      */
8589     disable : function(){
8590        this.disabled = true;
8591     },
8592
8593     /**
8594      * Enables the mask so that it can be displayed
8595      */
8596     enable : function(){
8597         this.disabled = false;
8598     },
8599     
8600     onLoadException : function()
8601     {
8602         Roo.log(arguments);
8603         
8604         if (typeof(arguments[3]) != 'undefined') {
8605             Roo.MessageBox.alert("Error loading",arguments[3]);
8606         } 
8607         /*
8608         try {
8609             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8610                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8611             }   
8612         } catch(e) {
8613             
8614         }
8615         */
8616     
8617         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8618     },
8619     // private
8620     onLoad : function()
8621     {
8622         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8623     },
8624
8625     // private
8626     onBeforeLoad : function(){
8627         if(!this.disabled){
8628             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8629         }
8630     },
8631
8632     // private
8633     destroy : function(){
8634         if(this.store){
8635             this.store.un('beforeload', this.onBeforeLoad, this);
8636             this.store.un('load', this.onLoad, this);
8637             this.store.un('loadexception', this.onLoadException, this);
8638         }else{
8639             var um = this.el.getUpdateManager();
8640             um.un('beforeupdate', this.onBeforeLoad, this);
8641             um.un('update', this.onLoad, this);
8642             um.un('failure', this.onLoad, this);
8643         }
8644     }
8645 };/**
8646  * @class Roo.bootstrap.Table
8647  * @licence LGBL
8648  * @extends Roo.bootstrap.Component
8649  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8650  * Similar to Roo.grid.Grid
8651  * <pre><code>
8652  var table = Roo.factory({
8653     xtype : 'Table',
8654     xns : Roo.bootstrap,
8655     autoSizeColumns: true,
8656     
8657     
8658     store : {
8659         xtype : 'Store',
8660         xns : Roo.data,
8661         remoteSort : true,
8662         sortInfo : { direction : 'ASC', field: 'name' },
8663         proxy : {
8664            xtype : 'HttpProxy',
8665            xns : Roo.data,
8666            method : 'GET',
8667            url : 'https://example.com/some.data.url.json'
8668         },
8669         reader : {
8670            xtype : 'JsonReader',
8671            xns : Roo.data,
8672            fields : [ 'id', 'name', whatever' ],
8673            id : 'id',
8674            root : 'data'
8675         }
8676     },
8677     cm : [
8678         {
8679             xtype : 'ColumnModel',
8680             xns : Roo.grid,
8681             align : 'center',
8682             cursor : 'pointer',
8683             dataIndex : 'is_in_group',
8684             header : "Name",
8685             sortable : true,
8686             renderer : function(v, x , r) {  
8687             
8688                 return String.format("{0}", v)
8689             }
8690             width : 3
8691         } // more columns..
8692     ],
8693     selModel : {
8694         xtype : 'RowSelectionModel',
8695         xns : Roo.bootstrap.Table
8696         // you can add listeners to catch selection change here....
8697     }
8698      
8699
8700  });
8701  // set any options
8702  grid.render(Roo.get("some-div"));
8703 </code></pre>
8704
8705 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8706
8707
8708
8709  *
8710  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8711  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8712  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8713  * 
8714  * @cfg {String} cls table class
8715  *
8716  * 
8717  * @cfg {boolean} striped Should the rows be alternative striped
8718  * @cfg {boolean} bordered Add borders to the table
8719  * @cfg {boolean} hover Add hover highlighting
8720  * @cfg {boolean} condensed Format condensed
8721  * @cfg {boolean} responsive Format condensed
8722  * @cfg {Boolean} loadMask (true|false) default false
8723  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8724  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8725  * @cfg {Boolean} rowSelection (true|false) default false
8726  * @cfg {Boolean} cellSelection (true|false) default false
8727  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8728  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8729  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8730  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8731  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8732  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8733  * 
8734  * @constructor
8735  * Create a new Table
8736  * @param {Object} config The config object
8737  */
8738
8739 Roo.bootstrap.Table = function(config)
8740 {
8741     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8742      
8743     // BC...
8744     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8745     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8746     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8747     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8748     
8749     this.view = this; // compat with grid.
8750     
8751     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8752     if (this.sm) {
8753         this.sm.grid = this;
8754         this.selModel = Roo.factory(this.sm, Roo.grid);
8755         this.sm = this.selModel;
8756         this.sm.xmodule = this.xmodule || false;
8757     }
8758     
8759     if (this.cm && typeof(this.cm.config) == 'undefined') {
8760         this.colModel = new Roo.grid.ColumnModel(this.cm);
8761         this.cm = this.colModel;
8762         this.cm.xmodule = this.xmodule || false;
8763     }
8764     if (this.store) {
8765         this.store= Roo.factory(this.store, Roo.data);
8766         this.ds = this.store;
8767         this.ds.xmodule = this.xmodule || false;
8768          
8769     }
8770     if (this.footer && this.store) {
8771         this.footer.dataSource = this.ds;
8772         this.footer = Roo.factory(this.footer);
8773     }
8774     
8775     /** @private */
8776     this.addEvents({
8777         /**
8778          * @event cellclick
8779          * Fires when a cell is clicked
8780          * @param {Roo.bootstrap.Table} this
8781          * @param {Roo.Element} el
8782          * @param {Number} rowIndex
8783          * @param {Number} columnIndex
8784          * @param {Roo.EventObject} e
8785          */
8786         "cellclick" : true,
8787         /**
8788          * @event celldblclick
8789          * Fires when a cell is double clicked
8790          * @param {Roo.bootstrap.Table} this
8791          * @param {Roo.Element} el
8792          * @param {Number} rowIndex
8793          * @param {Number} columnIndex
8794          * @param {Roo.EventObject} e
8795          */
8796         "celldblclick" : true,
8797         /**
8798          * @event rowclick
8799          * Fires when a row is clicked
8800          * @param {Roo.bootstrap.Table} this
8801          * @param {Roo.Element} el
8802          * @param {Number} rowIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "rowclick" : true,
8806         /**
8807          * @event rowdblclick
8808          * Fires when a row is double clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Roo.EventObject} e
8813          */
8814         "rowdblclick" : true,
8815         /**
8816          * @event mouseover
8817          * Fires when a mouseover occur
8818          * @param {Roo.bootstrap.Table} this
8819          * @param {Roo.Element} el
8820          * @param {Number} rowIndex
8821          * @param {Number} columnIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "mouseover" : true,
8825         /**
8826          * @event mouseout
8827          * Fires when a mouseout occur
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Number} columnIndex
8832          * @param {Roo.EventObject} e
8833          */
8834         "mouseout" : true,
8835         /**
8836          * @event rowclass
8837          * Fires when a row is rendered, so you can change add a style to it.
8838          * @param {Roo.bootstrap.Table} this
8839          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8840          */
8841         'rowclass' : true,
8842           /**
8843          * @event rowsrendered
8844          * Fires when all the  rows have been rendered
8845          * @param {Roo.bootstrap.Table} this
8846          */
8847         'rowsrendered' : true,
8848         /**
8849          * @event contextmenu
8850          * The raw contextmenu event for the entire grid.
8851          * @param {Roo.EventObject} e
8852          */
8853         "contextmenu" : true,
8854         /**
8855          * @event rowcontextmenu
8856          * Fires when a row is right clicked
8857          * @param {Roo.bootstrap.Table} this
8858          * @param {Number} rowIndex
8859          * @param {Roo.EventObject} e
8860          */
8861         "rowcontextmenu" : true,
8862         /**
8863          * @event cellcontextmenu
8864          * Fires when a cell is right clicked
8865          * @param {Roo.bootstrap.Table} this
8866          * @param {Number} rowIndex
8867          * @param {Number} cellIndex
8868          * @param {Roo.EventObject} e
8869          */
8870          "cellcontextmenu" : true,
8871          /**
8872          * @event headercontextmenu
8873          * Fires when a header is right clicked
8874          * @param {Roo.bootstrap.Table} this
8875          * @param {Number} columnIndex
8876          * @param {Roo.EventObject} e
8877          */
8878         "headercontextmenu" : true,
8879         /**
8880          * @event mousedown
8881          * The raw mousedown event for the entire grid.
8882          * @param {Roo.EventObject} e
8883          */
8884         "mousedown" : true
8885         
8886     });
8887 };
8888
8889 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8890     
8891     cls: false,
8892     
8893     striped : false,
8894     scrollBody : false,
8895     bordered: false,
8896     hover:  false,
8897     condensed : false,
8898     responsive : false,
8899     sm : false,
8900     cm : false,
8901     store : false,
8902     loadMask : false,
8903     footerShow : true,
8904     headerShow : true,
8905     enableColumnResize: true,
8906   
8907     rowSelection : false,
8908     cellSelection : false,
8909     layout : false,
8910
8911     minColumnWidth : 50,
8912     
8913     // Roo.Element - the tbody
8914     bodyEl: false,  // <tbody> Roo.Element - thead element    
8915     headEl: false,  // <thead> Roo.Element - thead element
8916     resizeProxy : false, // proxy element for dragging?
8917
8918
8919     
8920     container: false, // used by gridpanel...
8921     
8922     lazyLoad : false,
8923     
8924     CSS : Roo.util.CSS,
8925     
8926     auto_hide_footer : false,
8927     
8928     view: false, // actually points to this..
8929     
8930     getAutoCreate : function()
8931     {
8932         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8933         
8934         cfg = {
8935             tag: 'table',
8936             cls : 'table', 
8937             cn : []
8938         };
8939         // this get's auto added by panel.Grid
8940         if (this.scrollBody) {
8941             cfg.cls += ' table-body-fixed';
8942         }    
8943         if (this.striped) {
8944             cfg.cls += ' table-striped';
8945         }
8946         
8947         if (this.hover) {
8948             cfg.cls += ' table-hover';
8949         }
8950         if (this.bordered) {
8951             cfg.cls += ' table-bordered';
8952         }
8953         if (this.condensed) {
8954             cfg.cls += ' table-condensed';
8955         }
8956         
8957         if (this.responsive) {
8958             cfg.cls += ' table-responsive';
8959         }
8960         
8961         if (this.cls) {
8962             cfg.cls+=  ' ' +this.cls;
8963         }
8964         
8965         
8966         
8967         if (this.layout) {
8968             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8969         }
8970         
8971         if(this.store || this.cm){
8972             if(this.headerShow){
8973                 cfg.cn.push(this.renderHeader());
8974             }
8975             
8976             cfg.cn.push(this.renderBody());
8977             
8978             if(this.footerShow){
8979                 cfg.cn.push(this.renderFooter());
8980             }
8981             // where does this come from?
8982             //cfg.cls+=  ' TableGrid';
8983         }
8984         
8985         return { cn : [ cfg ] };
8986     },
8987     
8988     initEvents : function()
8989     {   
8990         if(!this.store || !this.cm){
8991             return;
8992         }
8993         if (this.selModel) {
8994             this.selModel.initEvents();
8995         }
8996         
8997         
8998         //Roo.log('initEvents with ds!!!!');
8999         
9000         this.bodyEl = this.el.select('tbody', true).first();
9001         this.headEl = this.el.select('thead', true).first();
9002         this.mainFoot = this.el.select('tfoot', true).first();
9003         
9004         
9005         
9006         
9007         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9008             e.on('click', this.sort, this);
9009         }, this);
9010         
9011         
9012         // why is this done????? = it breaks dialogs??
9013         //this.parent().el.setStyle('position', 'relative');
9014         
9015         
9016         if (this.footer) {
9017             this.footer.parentId = this.id;
9018             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9019             
9020             if(this.lazyLoad){
9021                 this.el.select('tfoot tr td').first().addClass('hide');
9022             }
9023         } 
9024         
9025         if(this.loadMask) {
9026             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9027         }
9028         
9029         this.store.on('load', this.onLoad, this);
9030         this.store.on('beforeload', this.onBeforeLoad, this);
9031         this.store.on('update', this.onUpdate, this);
9032         this.store.on('add', this.onAdd, this);
9033         this.store.on("clear", this.clear, this);
9034         
9035         this.el.on("contextmenu", this.onContextMenu, this);
9036         
9037         
9038         this.cm.on("headerchange", this.onHeaderChange, this);
9039         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9040
9041  //?? does bodyEl get replaced on render?
9042         this.bodyEl.on("click", this.onClick, this);
9043         this.bodyEl.on("dblclick", this.onDblClick, this);        
9044         this.bodyEl.on('scroll', this.onBodyScroll, this);
9045
9046         // guessing mainbody will work - this relays usually caught by selmodel at present.
9047         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9048   
9049   
9050         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9051         
9052   
9053         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9054             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9055         }
9056         
9057         this.initCSS();
9058     },
9059     // Compatibility with grid - we implement all the view features at present.
9060     getView : function()
9061     {
9062         return this;
9063     },
9064     
9065     initCSS : function()
9066     {
9067         
9068         
9069         var cm = this.cm, styles = [];
9070         this.CSS.removeStyleSheet(this.id + '-cssrules');
9071         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9072         // we can honour xs/sm/md/xl  as widths...
9073         // we first have to decide what widht we are currently at...
9074         var sz = Roo.getGridSize();
9075         
9076         var total = 0;
9077         var last = -1;
9078         var cols = []; // visable cols.
9079         var total_abs = 0;
9080         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9081             var w = cm.getColumnWidth(i, false);
9082             if(cm.isHidden(i)){
9083                 cols.push( { rel : false, abs : 0 });
9084                 continue;
9085             }
9086             if (w !== false) {
9087                 cols.push( { rel : false, abs : w });
9088                 total_abs += w;
9089                 last = i; // not really..
9090                 continue;
9091             }
9092             var w = cm.getColumnWidth(i, sz);
9093             if (w > 0) {
9094                 last = i
9095             }
9096             total += w;
9097             cols.push( { rel : w, abs : false });
9098         }
9099         
9100         var avail = this.bodyEl.dom.clientWidth - total_abs;
9101         
9102         var unitWidth = Math.floor(avail / total);
9103         var rem = avail - (unitWidth * total);
9104         
9105         var hidden, width, pos = 0 , splithide , left;
9106         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9107             
9108             hidden = 'display:none;';
9109             left = '';
9110             width  = 'width:0px;';
9111             splithide = '';
9112             if(!cm.isHidden(i)){
9113                 hidden = '';
9114                 
9115                 
9116                 // we can honour xs/sm/md/xl ?
9117                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9118                 if (w===0) {
9119                     hidden = 'display:none;';
9120                 }
9121                 // width should return a small number...
9122                 if (i == last) {
9123                     w+=rem; // add the remaining with..
9124                 }
9125                 pos += w;
9126                 left = "left:" + (pos -4) + "px;";
9127                 width = "width:" + w+ "px;";
9128                 
9129             }
9130             
9131             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9132             if (this.headEl) {
9133                 if (i == last) {
9134                     splithide = 'display:none;';
9135                 }
9136                 
9137                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9138                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9139                 );
9140             }
9141             
9142         }
9143         Roo.log(styles.join(''));
9144         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9145         
9146     },
9147     
9148     
9149     
9150     onContextMenu : function(e, t)
9151     {
9152         this.processEvent("contextmenu", e);
9153     },
9154     
9155     processEvent : function(name, e)
9156     {
9157         if (name != 'touchstart' ) {
9158             this.fireEvent(name, e);    
9159         }
9160         
9161         var t = e.getTarget();
9162         
9163         var cell = Roo.get(t);
9164         
9165         if(!cell){
9166             return;
9167         }
9168         
9169         if(cell.findParent('tfoot', false, true)){
9170             return;
9171         }
9172         
9173         if(cell.findParent('thead', false, true)){
9174             
9175             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9176                 cell = Roo.get(t).findParent('th', false, true);
9177                 if (!cell) {
9178                     Roo.log("failed to find th in thead?");
9179                     Roo.log(e.getTarget());
9180                     return;
9181                 }
9182             }
9183             
9184             var cellIndex = cell.dom.cellIndex;
9185             
9186             var ename = name == 'touchstart' ? 'click' : name;
9187             this.fireEvent("header" + ename, this, cellIndex, e);
9188             
9189             return;
9190         }
9191         
9192         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9193             cell = Roo.get(t).findParent('td', false, true);
9194             if (!cell) {
9195                 Roo.log("failed to find th in tbody?");
9196                 Roo.log(e.getTarget());
9197                 return;
9198             }
9199         }
9200         
9201         var row = cell.findParent('tr', false, true);
9202         var cellIndex = cell.dom.cellIndex;
9203         var rowIndex = row.dom.rowIndex - 1;
9204         
9205         if(row !== false){
9206             
9207             this.fireEvent("row" + name, this, rowIndex, e);
9208             
9209             if(cell !== false){
9210             
9211                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9212             }
9213         }
9214         
9215     },
9216     
9217     onMouseover : function(e, el)
9218     {
9219         var cell = Roo.get(el);
9220         
9221         if(!cell){
9222             return;
9223         }
9224         
9225         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9226             cell = cell.findParent('td', false, true);
9227         }
9228         
9229         var row = cell.findParent('tr', false, true);
9230         var cellIndex = cell.dom.cellIndex;
9231         var rowIndex = row.dom.rowIndex - 1; // start from 0
9232         
9233         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9234         
9235     },
9236     
9237     onMouseout : function(e, el)
9238     {
9239         var cell = Roo.get(el);
9240         
9241         if(!cell){
9242             return;
9243         }
9244         
9245         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9246             cell = cell.findParent('td', false, true);
9247         }
9248         
9249         var row = cell.findParent('tr', false, true);
9250         var cellIndex = cell.dom.cellIndex;
9251         var rowIndex = row.dom.rowIndex - 1; // start from 0
9252         
9253         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9254         
9255     },
9256     
9257     onClick : function(e, el)
9258     {
9259         var cell = Roo.get(el);
9260         
9261         if(!cell || (!this.cellSelection && !this.rowSelection)){
9262             return;
9263         }
9264         
9265         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9266             cell = cell.findParent('td', false, true);
9267         }
9268         
9269         if(!cell || typeof(cell) == 'undefined'){
9270             return;
9271         }
9272         
9273         var row = cell.findParent('tr', false, true);
9274         
9275         if(!row || typeof(row) == 'undefined'){
9276             return;
9277         }
9278         
9279         var cellIndex = cell.dom.cellIndex;
9280         var rowIndex = this.getRowIndex(row);
9281         
9282         // why??? - should these not be based on SelectionModel?
9283         //if(this.cellSelection){
9284             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9285         //}
9286         
9287         //if(this.rowSelection){
9288             this.fireEvent('rowclick', this, row, rowIndex, e);
9289         //}
9290          
9291     },
9292         
9293     onDblClick : function(e,el)
9294     {
9295         var cell = Roo.get(el);
9296         
9297         if(!cell || (!this.cellSelection && !this.rowSelection)){
9298             return;
9299         }
9300         
9301         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9302             cell = cell.findParent('td', false, true);
9303         }
9304         
9305         if(!cell || typeof(cell) == 'undefined'){
9306             return;
9307         }
9308         
9309         var row = cell.findParent('tr', false, true);
9310         
9311         if(!row || typeof(row) == 'undefined'){
9312             return;
9313         }
9314         
9315         var cellIndex = cell.dom.cellIndex;
9316         var rowIndex = this.getRowIndex(row);
9317         
9318         if(this.cellSelection){
9319             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9320         }
9321         
9322         if(this.rowSelection){
9323             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9324         }
9325     },
9326     findRowIndex : function(el)
9327     {
9328         var cell = Roo.get(el);
9329         if(!cell) {
9330             return false;
9331         }
9332         var row = cell.findParent('tr', false, true);
9333         
9334         if(!row || typeof(row) == 'undefined'){
9335             return false;
9336         }
9337         return this.getRowIndex(row);
9338     },
9339     sort : function(e,el)
9340     {
9341         var col = Roo.get(el);
9342         
9343         if(!col.hasClass('sortable')){
9344             return;
9345         }
9346         
9347         var sort = col.attr('sort');
9348         var dir = 'ASC';
9349         
9350         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9351             dir = 'DESC';
9352         }
9353         
9354         this.store.sortInfo = {field : sort, direction : dir};
9355         
9356         if (this.footer) {
9357             Roo.log("calling footer first");
9358             this.footer.onClick('first');
9359         } else {
9360         
9361             this.store.load({ params : { start : 0 } });
9362         }
9363     },
9364     
9365     renderHeader : function()
9366     {
9367         var header = {
9368             tag: 'thead',
9369             cn : []
9370         };
9371         
9372         var cm = this.cm;
9373         this.totalWidth = 0;
9374         
9375         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9376             
9377             var config = cm.config[i];
9378             
9379             var c = {
9380                 tag: 'th',
9381                 cls : 'x-hcol-' + i,
9382                 style : '',
9383                 
9384                 html: cm.getColumnHeader(i)
9385             };
9386             
9387             var tooltip = cm.getColumnTooltip(i);
9388             if (tooltip) {
9389                 c.tooltip = tooltip;
9390             }
9391             
9392             
9393             var hh = '';
9394             
9395             if(typeof(config.sortable) != 'undefined' && config.sortable){
9396                 c.cls += ' sortable';
9397                 c.html = '<i class="fa"></i>' + c.html;
9398             }
9399             
9400             // could use BS4 hidden-..-down 
9401             
9402             if(typeof(config.lgHeader) != 'undefined'){
9403                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9404             }
9405             
9406             if(typeof(config.mdHeader) != 'undefined'){
9407                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9408             }
9409             
9410             if(typeof(config.smHeader) != 'undefined'){
9411                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9412             }
9413             
9414             if(typeof(config.xsHeader) != 'undefined'){
9415                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9416             }
9417             
9418             if(hh.length){
9419                 c.html = hh;
9420             }
9421             
9422             if(typeof(config.tooltip) != 'undefined'){
9423                 c.tooltip = config.tooltip;
9424             }
9425             
9426             if(typeof(config.colspan) != 'undefined'){
9427                 c.colspan = config.colspan;
9428             }
9429             
9430             // hidden is handled by CSS now
9431             
9432             if(typeof(config.dataIndex) != 'undefined'){
9433                 c.sort = config.dataIndex;
9434             }
9435             
9436            
9437             
9438             if(typeof(config.align) != 'undefined' && config.align.length){
9439                 c.style += ' text-align:' + config.align + ';';
9440             }
9441             
9442             /* width is done in CSS
9443              *if(typeof(config.width) != 'undefined'){
9444                 c.style += ' width:' + config.width + 'px;';
9445                 this.totalWidth += config.width;
9446             } else {
9447                 this.totalWidth += 100; // assume minimum of 100 per column?
9448             }
9449             */
9450             
9451             if(typeof(config.cls) != 'undefined'){
9452                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9453             }
9454             // this is the bit that doesnt reall work at all...
9455             
9456            /*
9457             
9458             ['xs','sm','md','lg'].map(function(size){
9459                 
9460                 if(typeof(config[size]) == 'undefined'){
9461                     return;
9462                 }
9463                  
9464                 if (!config[size]) { // 0 = hidden
9465                     // BS 4 '0' is treated as hide that column and below.
9466                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9467                     return;
9468                 }
9469                 
9470                 c.cls += ' col-' + size + '-' + config[size] + (
9471                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9472                 );
9473                 
9474                 
9475             });
9476             */
9477             // at the end?
9478             
9479             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9480             
9481             
9482             
9483             
9484             header.cn.push(c)
9485         }
9486         
9487         return header;
9488     },
9489     
9490     renderBody : function()
9491     {
9492         var body = {
9493             tag: 'tbody',
9494             cn : [
9495                 {
9496                     tag: 'tr',
9497                     cn : [
9498                         {
9499                             tag : 'td',
9500                             colspan :  this.cm.getColumnCount()
9501                         }
9502                     ]
9503                 }
9504             ]
9505         };
9506         
9507         return body;
9508     },
9509     
9510     renderFooter : function()
9511     {
9512         var footer = {
9513             tag: 'tfoot',
9514             cn : [
9515                 {
9516                     tag: 'tr',
9517                     cn : [
9518                         {
9519                             tag : 'td',
9520                             colspan :  this.cm.getColumnCount()
9521                         }
9522                     ]
9523                 }
9524             ]
9525         };
9526         
9527         return footer;
9528     },
9529     
9530     
9531     
9532     onLoad : function()
9533     {
9534 //        Roo.log('ds onload');
9535         this.clear();
9536         
9537         var _this = this;
9538         var cm = this.cm;
9539         var ds = this.store;
9540         
9541         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9542             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9543             if (_this.store.sortInfo) {
9544                     
9545                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9546                     e.select('i', true).addClass(['fa-arrow-up']);
9547                 }
9548                 
9549                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9550                     e.select('i', true).addClass(['fa-arrow-down']);
9551                 }
9552             }
9553         });
9554         
9555         var tbody =  this.bodyEl;
9556               
9557         if(ds.getCount() > 0){
9558             ds.data.each(function(d,rowIndex){
9559                 var row =  this.renderRow(cm, ds, rowIndex);
9560                 
9561                 tbody.createChild(row);
9562                 
9563                 var _this = this;
9564                 
9565                 if(row.cellObjects.length){
9566                     Roo.each(row.cellObjects, function(r){
9567                         _this.renderCellObject(r);
9568                     })
9569                 }
9570                 
9571             }, this);
9572         }
9573         
9574         var tfoot = this.el.select('tfoot', true).first();
9575         
9576         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9577             
9578             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9579             
9580             var total = this.ds.getTotalCount();
9581             
9582             if(this.footer.pageSize < total){
9583                 this.mainFoot.show();
9584             }
9585         }
9586         
9587         Roo.each(this.el.select('tbody td', true).elements, function(e){
9588             e.on('mouseover', _this.onMouseover, _this);
9589         });
9590         
9591         Roo.each(this.el.select('tbody td', true).elements, function(e){
9592             e.on('mouseout', _this.onMouseout, _this);
9593         });
9594         this.fireEvent('rowsrendered', this);
9595         
9596         this.autoSize();
9597         
9598         this.initCSS(); /// resize cols
9599
9600         
9601     },
9602     
9603     
9604     onUpdate : function(ds,record)
9605     {
9606         this.refreshRow(record);
9607         this.autoSize();
9608     },
9609     
9610     onRemove : function(ds, record, index, isUpdate){
9611         if(isUpdate !== true){
9612             this.fireEvent("beforerowremoved", this, index, record);
9613         }
9614         var bt = this.bodyEl.dom;
9615         
9616         var rows = this.el.select('tbody > tr', true).elements;
9617         
9618         if(typeof(rows[index]) != 'undefined'){
9619             bt.removeChild(rows[index].dom);
9620         }
9621         
9622 //        if(bt.rows[index]){
9623 //            bt.removeChild(bt.rows[index]);
9624 //        }
9625         
9626         if(isUpdate !== true){
9627             //this.stripeRows(index);
9628             //this.syncRowHeights(index, index);
9629             //this.layout();
9630             this.fireEvent("rowremoved", this, index, record);
9631         }
9632     },
9633     
9634     onAdd : function(ds, records, rowIndex)
9635     {
9636         //Roo.log('on Add called');
9637         // - note this does not handle multiple adding very well..
9638         var bt = this.bodyEl.dom;
9639         for (var i =0 ; i < records.length;i++) {
9640             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9641             //Roo.log(records[i]);
9642             //Roo.log(this.store.getAt(rowIndex+i));
9643             this.insertRow(this.store, rowIndex + i, false);
9644             return;
9645         }
9646         
9647     },
9648     
9649     
9650     refreshRow : function(record){
9651         var ds = this.store, index;
9652         if(typeof record == 'number'){
9653             index = record;
9654             record = ds.getAt(index);
9655         }else{
9656             index = ds.indexOf(record);
9657             if (index < 0) {
9658                 return; // should not happen - but seems to 
9659             }
9660         }
9661         this.insertRow(ds, index, true);
9662         this.autoSize();
9663         this.onRemove(ds, record, index+1, true);
9664         this.autoSize();
9665         //this.syncRowHeights(index, index);
9666         //this.layout();
9667         this.fireEvent("rowupdated", this, index, record);
9668     },
9669     // private - called by RowSelection
9670     onRowSelect : function(rowIndex){
9671         var row = this.getRowDom(rowIndex);
9672         row.addClass(['bg-info','info']);
9673     },
9674     // private - called by RowSelection
9675     onRowDeselect : function(rowIndex)
9676     {
9677         if (rowIndex < 0) {
9678             return;
9679         }
9680         var row = this.getRowDom(rowIndex);
9681         row.removeClass(['bg-info','info']);
9682     },
9683       /**
9684      * Focuses the specified row.
9685      * @param {Number} row The row index
9686      */
9687     focusRow : function(row)
9688     {
9689         //Roo.log('GridView.focusRow');
9690         var x = this.bodyEl.dom.scrollLeft;
9691         this.focusCell(row, 0, false);
9692         this.bodyEl.dom.scrollLeft = x;
9693
9694     },
9695      /**
9696      * Focuses the specified cell.
9697      * @param {Number} row The row index
9698      * @param {Number} col The column index
9699      * @param {Boolean} hscroll false to disable horizontal scrolling
9700      */
9701     focusCell : function(row, col, hscroll)
9702     {
9703         //Roo.log('GridView.focusCell');
9704         var el = this.ensureVisible(row, col, hscroll);
9705         // not sure what focusEL achives = it's a <a> pos relative 
9706         //this.focusEl.alignTo(el, "tl-tl");
9707         //if(Roo.isGecko){
9708         //    this.focusEl.focus();
9709         //}else{
9710         //    this.focusEl.focus.defer(1, this.focusEl);
9711         //}
9712     },
9713     
9714      /**
9715      * Scrolls the specified cell into view
9716      * @param {Number} row The row index
9717      * @param {Number} col The column index
9718      * @param {Boolean} hscroll false to disable horizontal scrolling
9719      */
9720     ensureVisible : function(row, col, hscroll)
9721     {
9722         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9723         //return null; //disable for testing.
9724         if(typeof row != "number"){
9725             row = row.rowIndex;
9726         }
9727         if(row < 0 && row >= this.ds.getCount()){
9728             return  null;
9729         }
9730         col = (col !== undefined ? col : 0);
9731         var cm = this.cm;
9732         while(cm.isHidden(col)){
9733             col++;
9734         }
9735
9736         var el = this.getCellDom(row, col);
9737         if(!el){
9738             return null;
9739         }
9740         var c = this.bodyEl.dom;
9741
9742         var ctop = parseInt(el.offsetTop, 10);
9743         var cleft = parseInt(el.offsetLeft, 10);
9744         var cbot = ctop + el.offsetHeight;
9745         var cright = cleft + el.offsetWidth;
9746
9747         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9748         var ch = 0; //?? header is not withing the area?
9749         var stop = parseInt(c.scrollTop, 10);
9750         var sleft = parseInt(c.scrollLeft, 10);
9751         var sbot = stop + ch;
9752         var sright = sleft + c.clientWidth;
9753         /*
9754         Roo.log('GridView.ensureVisible:' +
9755                 ' ctop:' + ctop +
9756                 ' c.clientHeight:' + c.clientHeight +
9757                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9758                 ' stop:' + stop +
9759                 ' cbot:' + cbot +
9760                 ' sbot:' + sbot +
9761                 ' ch:' + ch  
9762                 );
9763         */
9764         if(ctop < stop){
9765             c.scrollTop = ctop;
9766             //Roo.log("set scrolltop to ctop DISABLE?");
9767         }else if(cbot > sbot){
9768             //Roo.log("set scrolltop to cbot-ch");
9769             c.scrollTop = cbot-ch;
9770         }
9771
9772         if(hscroll !== false){
9773             if(cleft < sleft){
9774                 c.scrollLeft = cleft;
9775             }else if(cright > sright){
9776                 c.scrollLeft = cright-c.clientWidth;
9777             }
9778         }
9779
9780         return el;
9781     },
9782     
9783     
9784     insertRow : function(dm, rowIndex, isUpdate){
9785         
9786         if(!isUpdate){
9787             this.fireEvent("beforerowsinserted", this, rowIndex);
9788         }
9789             //var s = this.getScrollState();
9790         var row = this.renderRow(this.cm, this.store, rowIndex);
9791         // insert before rowIndex..
9792         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9793         
9794         var _this = this;
9795                 
9796         if(row.cellObjects.length){
9797             Roo.each(row.cellObjects, function(r){
9798                 _this.renderCellObject(r);
9799             })
9800         }
9801             
9802         if(!isUpdate){
9803             this.fireEvent("rowsinserted", this, rowIndex);
9804             //this.syncRowHeights(firstRow, lastRow);
9805             //this.stripeRows(firstRow);
9806             //this.layout();
9807         }
9808         
9809     },
9810     
9811     
9812     getRowDom : function(rowIndex)
9813     {
9814         var rows = this.el.select('tbody > tr', true).elements;
9815         
9816         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9817         
9818     },
9819     getCellDom : function(rowIndex, colIndex)
9820     {
9821         var row = this.getRowDom(rowIndex);
9822         if (row === false) {
9823             return false;
9824         }
9825         var cols = row.select('td', true).elements;
9826         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9827         
9828     },
9829     
9830     // returns the object tree for a tr..
9831   
9832     
9833     renderRow : function(cm, ds, rowIndex) 
9834     {
9835         var d = ds.getAt(rowIndex);
9836         
9837         var row = {
9838             tag : 'tr',
9839             cls : 'x-row-' + rowIndex,
9840             cn : []
9841         };
9842             
9843         var cellObjects = [];
9844         
9845         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9846             var config = cm.config[i];
9847             
9848             var renderer = cm.getRenderer(i);
9849             var value = '';
9850             var id = false;
9851             
9852             if(typeof(renderer) !== 'undefined'){
9853                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9854             }
9855             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9856             // and are rendered into the cells after the row is rendered - using the id for the element.
9857             
9858             if(typeof(value) === 'object'){
9859                 id = Roo.id();
9860                 cellObjects.push({
9861                     container : id,
9862                     cfg : value 
9863                 })
9864             }
9865             
9866             var rowcfg = {
9867                 record: d,
9868                 rowIndex : rowIndex,
9869                 colIndex : i,
9870                 rowClass : ''
9871             };
9872
9873             this.fireEvent('rowclass', this, rowcfg);
9874             
9875             var td = {
9876                 tag: 'td',
9877                 // this might end up displaying HTML?
9878                 // this is too messy... - better to only do it on columsn you know are going to be too long
9879                 //tooltip : (typeof(value) === 'object') ? '' : value,
9880                 cls : rowcfg.rowClass + ' x-col-' + i,
9881                 style: '',
9882                 html: (typeof(value) === 'object') ? '' : value
9883             };
9884             
9885             if (id) {
9886                 td.id = id;
9887             }
9888             
9889             if(typeof(config.colspan) != 'undefined'){
9890                 td.colspan = config.colspan;
9891             }
9892             
9893             
9894             
9895             if(typeof(config.align) != 'undefined' && config.align.length){
9896                 td.style += ' text-align:' + config.align + ';';
9897             }
9898             if(typeof(config.valign) != 'undefined' && config.valign.length){
9899                 td.style += ' vertical-align:' + config.valign + ';';
9900             }
9901             /*
9902             if(typeof(config.width) != 'undefined'){
9903                 td.style += ' width:' +  config.width + 'px;';
9904             }
9905             */
9906             
9907             if(typeof(config.cursor) != 'undefined'){
9908                 td.style += ' cursor:' +  config.cursor + ';';
9909             }
9910             
9911             if(typeof(config.cls) != 'undefined'){
9912                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9913             }
9914             /*
9915             ['xs','sm','md','lg'].map(function(size){
9916                 
9917                 if(typeof(config[size]) == 'undefined'){
9918                     return;
9919                 }
9920                 
9921                 
9922                   
9923                 if (!config[size]) { // 0 = hidden
9924                     // BS 4 '0' is treated as hide that column and below.
9925                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9926                     return;
9927                 }
9928                 
9929                 td.cls += ' col-' + size + '-' + config[size] + (
9930                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9931                 );
9932                  
9933
9934             });
9935             */
9936             row.cn.push(td);
9937            
9938         }
9939         
9940         row.cellObjects = cellObjects;
9941         
9942         return row;
9943           
9944     },
9945     
9946     
9947     
9948     onBeforeLoad : function()
9949     {
9950         
9951     },
9952      /**
9953      * Remove all rows
9954      */
9955     clear : function()
9956     {
9957         this.el.select('tbody', true).first().dom.innerHTML = '';
9958     },
9959     /**
9960      * Show or hide a row.
9961      * @param {Number} rowIndex to show or hide
9962      * @param {Boolean} state hide
9963      */
9964     setRowVisibility : function(rowIndex, state)
9965     {
9966         var bt = this.bodyEl.dom;
9967         
9968         var rows = this.el.select('tbody > tr', true).elements;
9969         
9970         if(typeof(rows[rowIndex]) == 'undefined'){
9971             return;
9972         }
9973         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9974         
9975     },
9976     
9977     
9978     getSelectionModel : function(){
9979         if(!this.selModel){
9980             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9981         }
9982         return this.selModel;
9983     },
9984     /*
9985      * Render the Roo.bootstrap object from renderder
9986      */
9987     renderCellObject : function(r)
9988     {
9989         var _this = this;
9990         
9991         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9992         
9993         var t = r.cfg.render(r.container);
9994         
9995         if(r.cfg.cn){
9996             Roo.each(r.cfg.cn, function(c){
9997                 var child = {
9998                     container: t.getChildContainer(),
9999                     cfg: c
10000                 };
10001                 _this.renderCellObject(child);
10002             })
10003         }
10004     },
10005     /**
10006      * get the Row Index from a dom element.
10007      * @param {Roo.Element} row The row to look for
10008      * @returns {Number} the row
10009      */
10010     getRowIndex : function(row)
10011     {
10012         var rowIndex = -1;
10013         
10014         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10015             if(el != row){
10016                 return;
10017             }
10018             
10019             rowIndex = index;
10020         });
10021         
10022         return rowIndex;
10023     },
10024     /**
10025      * get the header TH element for columnIndex
10026      * @param {Number} columnIndex
10027      * @returns {Roo.Element}
10028      */
10029     getHeaderIndex: function(colIndex)
10030     {
10031         var cols = this.headEl.select('th', true).elements;
10032         return cols[colIndex]; 
10033     },
10034     /**
10035      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10036      * @param {domElement} cell to look for
10037      * @returns {Number} the column
10038      */
10039     getCellIndex : function(cell)
10040     {
10041         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10042         if(id){
10043             return parseInt(id[1], 10);
10044         }
10045         return 0;
10046     },
10047      /**
10048      * Returns the grid's underlying element = used by panel.Grid
10049      * @return {Element} The element
10050      */
10051     getGridEl : function(){
10052         return this.el;
10053     },
10054      /**
10055      * Forces a resize - used by panel.Grid
10056      * @return {Element} The element
10057      */
10058     autoSize : function()
10059     {
10060         //var ctr = Roo.get(this.container.dom.parentElement);
10061         var ctr = Roo.get(this.el.dom);
10062         
10063         var thd = this.getGridEl().select('thead',true).first();
10064         var tbd = this.getGridEl().select('tbody', true).first();
10065         var tfd = this.getGridEl().select('tfoot', true).first();
10066         
10067         var cw = ctr.getWidth();
10068         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10069         
10070         if (tbd) {
10071             
10072             tbd.setWidth(ctr.getWidth());
10073             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10074             // this needs fixing for various usage - currently only hydra job advers I think..
10075             //tdb.setHeight(
10076             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10077             //); 
10078             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10079             cw -= barsize;
10080         }
10081         cw = Math.max(cw, this.totalWidth);
10082         this.getGridEl().select('tbody tr',true).setWidth(cw);
10083         this.initCSS();
10084         
10085         // resize 'expandable coloumn?
10086         
10087         return; // we doe not have a view in this design..
10088         
10089     },
10090     onBodyScroll: function()
10091     {
10092         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10093         if(this.headEl){
10094             this.headEl.setStyle({
10095                 'position' : 'relative',
10096                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10097             });
10098         }
10099         
10100         if(this.lazyLoad){
10101             
10102             var scrollHeight = this.bodyEl.dom.scrollHeight;
10103             
10104             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10105             
10106             var height = this.bodyEl.getHeight();
10107             
10108             if(scrollHeight - height == scrollTop) {
10109                 
10110                 var total = this.ds.getTotalCount();
10111                 
10112                 if(this.footer.cursor + this.footer.pageSize < total){
10113                     
10114                     this.footer.ds.load({
10115                         params : {
10116                             start : this.footer.cursor + this.footer.pageSize,
10117                             limit : this.footer.pageSize
10118                         },
10119                         add : true
10120                     });
10121                 }
10122             }
10123             
10124         }
10125     },
10126     onColumnSplitterMoved : function(i, diff)
10127     {
10128         this.userResized = true;
10129         
10130         var cm = this.colModel;
10131         
10132         var w = this.getHeaderIndex(i).getWidth() + diff;
10133         
10134         
10135         cm.setColumnWidth(i, w, true);
10136         this.initCSS();
10137         //var cid = cm.getColumnId(i); << not used in this version?
10138        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10139         
10140         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10141         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10142         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10143 */
10144         //this.updateSplitters();
10145         //this.layout(); << ??
10146         this.fireEvent("columnresize", i, w);
10147     },
10148     onHeaderChange : function()
10149     {
10150         var header = this.renderHeader();
10151         var table = this.el.select('table', true).first();
10152         
10153         this.headEl.remove();
10154         this.headEl = table.createChild(header, this.bodyEl, false);
10155         
10156         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10157             e.on('click', this.sort, this);
10158         }, this);
10159         
10160         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10161             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10162         }
10163         
10164     },
10165     
10166     onHiddenChange : function(colModel, colIndex, hidden)
10167     {
10168         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10169         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10170         
10171         this.CSS.updateRule(thSelector, "display", "");
10172         this.CSS.updateRule(tdSelector, "display", "");
10173         
10174         if(hidden){
10175             this.CSS.updateRule(thSelector, "display", "none");
10176             this.CSS.updateRule(tdSelector, "display", "none");
10177         }
10178         
10179         this.onHeaderChange();
10180         this.onLoad();
10181     },
10182     
10183     setColumnWidth: function(col_index, width)
10184     {
10185         // width = "md-2 xs-2..."
10186         if(!this.colModel.config[col_index]) {
10187             return;
10188         }
10189         
10190         var w = width.split(" ");
10191         
10192         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10193         
10194         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10195         
10196         
10197         for(var j = 0; j < w.length; j++) {
10198             
10199             if(!w[j]) {
10200                 continue;
10201             }
10202             
10203             var size_cls = w[j].split("-");
10204             
10205             if(!Number.isInteger(size_cls[1] * 1)) {
10206                 continue;
10207             }
10208             
10209             if(!this.colModel.config[col_index][size_cls[0]]) {
10210                 continue;
10211             }
10212             
10213             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10214                 continue;
10215             }
10216             
10217             h_row[0].classList.replace(
10218                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10219                 "col-"+size_cls[0]+"-"+size_cls[1]
10220             );
10221             
10222             for(var i = 0; i < rows.length; i++) {
10223                 
10224                 var size_cls = w[j].split("-");
10225                 
10226                 if(!Number.isInteger(size_cls[1] * 1)) {
10227                     continue;
10228                 }
10229                 
10230                 if(!this.colModel.config[col_index][size_cls[0]]) {
10231                     continue;
10232                 }
10233                 
10234                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10235                     continue;
10236                 }
10237                 
10238                 rows[i].classList.replace(
10239                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10240                     "col-"+size_cls[0]+"-"+size_cls[1]
10241                 );
10242             }
10243             
10244             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10245         }
10246     }
10247 });
10248
10249 // currently only used to find the split on drag.. 
10250 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10251
10252 /**
10253  * @depricated
10254 */
10255 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10256 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10257 /*
10258  * - LGPL
10259  *
10260  * table cell
10261  * 
10262  */
10263
10264 /**
10265  * @class Roo.bootstrap.TableCell
10266  * @extends Roo.bootstrap.Component
10267  * Bootstrap TableCell class
10268  * @cfg {String} html cell contain text
10269  * @cfg {String} cls cell class
10270  * @cfg {String} tag cell tag (td|th) default td
10271  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10272  * @cfg {String} align Aligns the content in a cell
10273  * @cfg {String} axis Categorizes cells
10274  * @cfg {String} bgcolor Specifies the background color of a cell
10275  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10276  * @cfg {Number} colspan Specifies the number of columns a cell should span
10277  * @cfg {String} headers Specifies one or more header cells a cell is related to
10278  * @cfg {Number} height Sets the height of a cell
10279  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10280  * @cfg {Number} rowspan Sets the number of rows a cell should span
10281  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10282  * @cfg {String} valign Vertical aligns the content in a cell
10283  * @cfg {Number} width Specifies the width of a cell
10284  * 
10285  * @constructor
10286  * Create a new TableCell
10287  * @param {Object} config The config object
10288  */
10289
10290 Roo.bootstrap.TableCell = function(config){
10291     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10292 };
10293
10294 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10295     
10296     html: false,
10297     cls: false,
10298     tag: false,
10299     abbr: false,
10300     align: false,
10301     axis: false,
10302     bgcolor: false,
10303     charoff: false,
10304     colspan: false,
10305     headers: false,
10306     height: false,
10307     nowrap: false,
10308     rowspan: false,
10309     scope: false,
10310     valign: false,
10311     width: false,
10312     
10313     
10314     getAutoCreate : function(){
10315         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10316         
10317         cfg = {
10318             tag: 'td'
10319         };
10320         
10321         if(this.tag){
10322             cfg.tag = this.tag;
10323         }
10324         
10325         if (this.html) {
10326             cfg.html=this.html
10327         }
10328         if (this.cls) {
10329             cfg.cls=this.cls
10330         }
10331         if (this.abbr) {
10332             cfg.abbr=this.abbr
10333         }
10334         if (this.align) {
10335             cfg.align=this.align
10336         }
10337         if (this.axis) {
10338             cfg.axis=this.axis
10339         }
10340         if (this.bgcolor) {
10341             cfg.bgcolor=this.bgcolor
10342         }
10343         if (this.charoff) {
10344             cfg.charoff=this.charoff
10345         }
10346         if (this.colspan) {
10347             cfg.colspan=this.colspan
10348         }
10349         if (this.headers) {
10350             cfg.headers=this.headers
10351         }
10352         if (this.height) {
10353             cfg.height=this.height
10354         }
10355         if (this.nowrap) {
10356             cfg.nowrap=this.nowrap
10357         }
10358         if (this.rowspan) {
10359             cfg.rowspan=this.rowspan
10360         }
10361         if (this.scope) {
10362             cfg.scope=this.scope
10363         }
10364         if (this.valign) {
10365             cfg.valign=this.valign
10366         }
10367         if (this.width) {
10368             cfg.width=this.width
10369         }
10370         
10371         
10372         return cfg;
10373     }
10374    
10375 });
10376
10377  
10378
10379  /*
10380  * - LGPL
10381  *
10382  * table row
10383  * 
10384  */
10385
10386 /**
10387  * @class Roo.bootstrap.TableRow
10388  * @extends Roo.bootstrap.Component
10389  * Bootstrap TableRow class
10390  * @cfg {String} cls row class
10391  * @cfg {String} align Aligns the content in a table row
10392  * @cfg {String} bgcolor Specifies a background color for a table row
10393  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10394  * @cfg {String} valign Vertical aligns the content in a table row
10395  * 
10396  * @constructor
10397  * Create a new TableRow
10398  * @param {Object} config The config object
10399  */
10400
10401 Roo.bootstrap.TableRow = function(config){
10402     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10403 };
10404
10405 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10406     
10407     cls: false,
10408     align: false,
10409     bgcolor: false,
10410     charoff: false,
10411     valign: false,
10412     
10413     getAutoCreate : function(){
10414         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10415         
10416         cfg = {
10417             tag: 'tr'
10418         };
10419             
10420         if(this.cls){
10421             cfg.cls = this.cls;
10422         }
10423         if(this.align){
10424             cfg.align = this.align;
10425         }
10426         if(this.bgcolor){
10427             cfg.bgcolor = this.bgcolor;
10428         }
10429         if(this.charoff){
10430             cfg.charoff = this.charoff;
10431         }
10432         if(this.valign){
10433             cfg.valign = this.valign;
10434         }
10435         
10436         return cfg;
10437     }
10438    
10439 });
10440
10441  
10442
10443  /*
10444  * - LGPL
10445  *
10446  * table body
10447  * 
10448  */
10449
10450 /**
10451  * @class Roo.bootstrap.TableBody
10452  * @extends Roo.bootstrap.Component
10453  * Bootstrap TableBody class
10454  * @cfg {String} cls element class
10455  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10456  * @cfg {String} align Aligns the content inside the element
10457  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10458  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10459  * 
10460  * @constructor
10461  * Create a new TableBody
10462  * @param {Object} config The config object
10463  */
10464
10465 Roo.bootstrap.TableBody = function(config){
10466     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10467 };
10468
10469 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10470     
10471     cls: false,
10472     tag: false,
10473     align: false,
10474     charoff: false,
10475     valign: false,
10476     
10477     getAutoCreate : function(){
10478         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10479         
10480         cfg = {
10481             tag: 'tbody'
10482         };
10483             
10484         if (this.cls) {
10485             cfg.cls=this.cls
10486         }
10487         if(this.tag){
10488             cfg.tag = this.tag;
10489         }
10490         
10491         if(this.align){
10492             cfg.align = this.align;
10493         }
10494         if(this.charoff){
10495             cfg.charoff = this.charoff;
10496         }
10497         if(this.valign){
10498             cfg.valign = this.valign;
10499         }
10500         
10501         return cfg;
10502     }
10503     
10504     
10505 //    initEvents : function()
10506 //    {
10507 //        
10508 //        if(!this.store){
10509 //            return;
10510 //        }
10511 //        
10512 //        this.store = Roo.factory(this.store, Roo.data);
10513 //        this.store.on('load', this.onLoad, this);
10514 //        
10515 //        this.store.load();
10516 //        
10517 //    },
10518 //    
10519 //    onLoad: function () 
10520 //    {   
10521 //        this.fireEvent('load', this);
10522 //    }
10523 //    
10524 //   
10525 });
10526
10527  
10528
10529  /*
10530  * Based on:
10531  * Ext JS Library 1.1.1
10532  * Copyright(c) 2006-2007, Ext JS, LLC.
10533  *
10534  * Originally Released Under LGPL - original licence link has changed is not relivant.
10535  *
10536  * Fork - LGPL
10537  * <script type="text/javascript">
10538  */
10539
10540 // as we use this in bootstrap.
10541 Roo.namespace('Roo.form');
10542  /**
10543  * @class Roo.form.Action
10544  * Internal Class used to handle form actions
10545  * @constructor
10546  * @param {Roo.form.BasicForm} el The form element or its id
10547  * @param {Object} config Configuration options
10548  */
10549
10550  
10551  
10552 // define the action interface
10553 Roo.form.Action = function(form, options){
10554     this.form = form;
10555     this.options = options || {};
10556 };
10557 /**
10558  * Client Validation Failed
10559  * @const 
10560  */
10561 Roo.form.Action.CLIENT_INVALID = 'client';
10562 /**
10563  * Server Validation Failed
10564  * @const 
10565  */
10566 Roo.form.Action.SERVER_INVALID = 'server';
10567  /**
10568  * Connect to Server Failed
10569  * @const 
10570  */
10571 Roo.form.Action.CONNECT_FAILURE = 'connect';
10572 /**
10573  * Reading Data from Server Failed
10574  * @const 
10575  */
10576 Roo.form.Action.LOAD_FAILURE = 'load';
10577
10578 Roo.form.Action.prototype = {
10579     type : 'default',
10580     failureType : undefined,
10581     response : undefined,
10582     result : undefined,
10583
10584     // interface method
10585     run : function(options){
10586
10587     },
10588
10589     // interface method
10590     success : function(response){
10591
10592     },
10593
10594     // interface method
10595     handleResponse : function(response){
10596
10597     },
10598
10599     // default connection failure
10600     failure : function(response){
10601         
10602         this.response = response;
10603         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10604         this.form.afterAction(this, false);
10605     },
10606
10607     processResponse : function(response){
10608         this.response = response;
10609         if(!response.responseText){
10610             return true;
10611         }
10612         this.result = this.handleResponse(response);
10613         return this.result;
10614     },
10615
10616     // utility functions used internally
10617     getUrl : function(appendParams){
10618         var url = this.options.url || this.form.url || this.form.el.dom.action;
10619         if(appendParams){
10620             var p = this.getParams();
10621             if(p){
10622                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10623             }
10624         }
10625         return url;
10626     },
10627
10628     getMethod : function(){
10629         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10630     },
10631
10632     getParams : function(){
10633         var bp = this.form.baseParams;
10634         var p = this.options.params;
10635         if(p){
10636             if(typeof p == "object"){
10637                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10638             }else if(typeof p == 'string' && bp){
10639                 p += '&' + Roo.urlEncode(bp);
10640             }
10641         }else if(bp){
10642             p = Roo.urlEncode(bp);
10643         }
10644         return p;
10645     },
10646
10647     createCallback : function(){
10648         return {
10649             success: this.success,
10650             failure: this.failure,
10651             scope: this,
10652             timeout: (this.form.timeout*1000),
10653             upload: this.form.fileUpload ? this.success : undefined
10654         };
10655     }
10656 };
10657
10658 Roo.form.Action.Submit = function(form, options){
10659     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10660 };
10661
10662 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10663     type : 'submit',
10664
10665     haveProgress : false,
10666     uploadComplete : false,
10667     
10668     // uploadProgress indicator.
10669     uploadProgress : function()
10670     {
10671         if (!this.form.progressUrl) {
10672             return;
10673         }
10674         
10675         if (!this.haveProgress) {
10676             Roo.MessageBox.progress("Uploading", "Uploading");
10677         }
10678         if (this.uploadComplete) {
10679            Roo.MessageBox.hide();
10680            return;
10681         }
10682         
10683         this.haveProgress = true;
10684    
10685         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10686         
10687         var c = new Roo.data.Connection();
10688         c.request({
10689             url : this.form.progressUrl,
10690             params: {
10691                 id : uid
10692             },
10693             method: 'GET',
10694             success : function(req){
10695                //console.log(data);
10696                 var rdata = false;
10697                 var edata;
10698                 try  {
10699                    rdata = Roo.decode(req.responseText)
10700                 } catch (e) {
10701                     Roo.log("Invalid data from server..");
10702                     Roo.log(edata);
10703                     return;
10704                 }
10705                 if (!rdata || !rdata.success) {
10706                     Roo.log(rdata);
10707                     Roo.MessageBox.alert(Roo.encode(rdata));
10708                     return;
10709                 }
10710                 var data = rdata.data;
10711                 
10712                 if (this.uploadComplete) {
10713                    Roo.MessageBox.hide();
10714                    return;
10715                 }
10716                    
10717                 if (data){
10718                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10719                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10720                     );
10721                 }
10722                 this.uploadProgress.defer(2000,this);
10723             },
10724        
10725             failure: function(data) {
10726                 Roo.log('progress url failed ');
10727                 Roo.log(data);
10728             },
10729             scope : this
10730         });
10731            
10732     },
10733     
10734     
10735     run : function()
10736     {
10737         // run get Values on the form, so it syncs any secondary forms.
10738         this.form.getValues();
10739         
10740         var o = this.options;
10741         var method = this.getMethod();
10742         var isPost = method == 'POST';
10743         if(o.clientValidation === false || this.form.isValid()){
10744             
10745             if (this.form.progressUrl) {
10746                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10747                     (new Date() * 1) + '' + Math.random());
10748                     
10749             } 
10750             
10751             
10752             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10753                 form:this.form.el.dom,
10754                 url:this.getUrl(!isPost),
10755                 method: method,
10756                 params:isPost ? this.getParams() : null,
10757                 isUpload: this.form.fileUpload,
10758                 formData : this.form.formData
10759             }));
10760             
10761             this.uploadProgress();
10762
10763         }else if (o.clientValidation !== false){ // client validation failed
10764             this.failureType = Roo.form.Action.CLIENT_INVALID;
10765             this.form.afterAction(this, false);
10766         }
10767     },
10768
10769     success : function(response)
10770     {
10771         this.uploadComplete= true;
10772         if (this.haveProgress) {
10773             Roo.MessageBox.hide();
10774         }
10775         
10776         
10777         var result = this.processResponse(response);
10778         if(result === true || result.success){
10779             this.form.afterAction(this, true);
10780             return;
10781         }
10782         if(result.errors){
10783             this.form.markInvalid(result.errors);
10784             this.failureType = Roo.form.Action.SERVER_INVALID;
10785         }
10786         this.form.afterAction(this, false);
10787     },
10788     failure : function(response)
10789     {
10790         this.uploadComplete= true;
10791         if (this.haveProgress) {
10792             Roo.MessageBox.hide();
10793         }
10794         
10795         this.response = response;
10796         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10797         this.form.afterAction(this, false);
10798     },
10799     
10800     handleResponse : function(response){
10801         if(this.form.errorReader){
10802             var rs = this.form.errorReader.read(response);
10803             var errors = [];
10804             if(rs.records){
10805                 for(var i = 0, len = rs.records.length; i < len; i++) {
10806                     var r = rs.records[i];
10807                     errors[i] = r.data;
10808                 }
10809             }
10810             if(errors.length < 1){
10811                 errors = null;
10812             }
10813             return {
10814                 success : rs.success,
10815                 errors : errors
10816             };
10817         }
10818         var ret = false;
10819         try {
10820             ret = Roo.decode(response.responseText);
10821         } catch (e) {
10822             ret = {
10823                 success: false,
10824                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10825                 errors : []
10826             };
10827         }
10828         return ret;
10829         
10830     }
10831 });
10832
10833
10834 Roo.form.Action.Load = function(form, options){
10835     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10836     this.reader = this.form.reader;
10837 };
10838
10839 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10840     type : 'load',
10841
10842     run : function(){
10843         
10844         Roo.Ajax.request(Roo.apply(
10845                 this.createCallback(), {
10846                     method:this.getMethod(),
10847                     url:this.getUrl(false),
10848                     params:this.getParams()
10849         }));
10850     },
10851
10852     success : function(response){
10853         
10854         var result = this.processResponse(response);
10855         if(result === true || !result.success || !result.data){
10856             this.failureType = Roo.form.Action.LOAD_FAILURE;
10857             this.form.afterAction(this, false);
10858             return;
10859         }
10860         this.form.clearInvalid();
10861         this.form.setValues(result.data);
10862         this.form.afterAction(this, true);
10863     },
10864
10865     handleResponse : function(response){
10866         if(this.form.reader){
10867             var rs = this.form.reader.read(response);
10868             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10869             return {
10870                 success : rs.success,
10871                 data : data
10872             };
10873         }
10874         return Roo.decode(response.responseText);
10875     }
10876 });
10877
10878 Roo.form.Action.ACTION_TYPES = {
10879     'load' : Roo.form.Action.Load,
10880     'submit' : Roo.form.Action.Submit
10881 };/*
10882  * - LGPL
10883  *
10884  * form
10885  *
10886  */
10887
10888 /**
10889  * @class Roo.bootstrap.Form
10890  * @extends Roo.bootstrap.Component
10891  * Bootstrap Form class
10892  * @cfg {String} method  GET | POST (default POST)
10893  * @cfg {String} labelAlign top | left (default top)
10894  * @cfg {String} align left  | right - for navbars
10895  * @cfg {Boolean} loadMask load mask when submit (default true)
10896
10897  *
10898  * @constructor
10899  * Create a new Form
10900  * @param {Object} config The config object
10901  */
10902
10903
10904 Roo.bootstrap.Form = function(config){
10905     
10906     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10907     
10908     Roo.bootstrap.Form.popover.apply();
10909     
10910     this.addEvents({
10911         /**
10912          * @event clientvalidation
10913          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10914          * @param {Form} this
10915          * @param {Boolean} valid true if the form has passed client-side validation
10916          */
10917         clientvalidation: true,
10918         /**
10919          * @event beforeaction
10920          * Fires before any action is performed. Return false to cancel the action.
10921          * @param {Form} this
10922          * @param {Action} action The action to be performed
10923          */
10924         beforeaction: true,
10925         /**
10926          * @event actionfailed
10927          * Fires when an action fails.
10928          * @param {Form} this
10929          * @param {Action} action The action that failed
10930          */
10931         actionfailed : true,
10932         /**
10933          * @event actioncomplete
10934          * Fires when an action is completed.
10935          * @param {Form} this
10936          * @param {Action} action The action that completed
10937          */
10938         actioncomplete : true
10939     });
10940 };
10941
10942 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10943
10944      /**
10945      * @cfg {String} method
10946      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10947      */
10948     method : 'POST',
10949     /**
10950      * @cfg {String} url
10951      * The URL to use for form actions if one isn't supplied in the action options.
10952      */
10953     /**
10954      * @cfg {Boolean} fileUpload
10955      * Set to true if this form is a file upload.
10956      */
10957
10958     /**
10959      * @cfg {Object} baseParams
10960      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10961      */
10962
10963     /**
10964      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10965      */
10966     timeout: 30,
10967     /**
10968      * @cfg {Sting} align (left|right) for navbar forms
10969      */
10970     align : 'left',
10971
10972     // private
10973     activeAction : null,
10974
10975     /**
10976      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10977      * element by passing it or its id or mask the form itself by passing in true.
10978      * @type Mixed
10979      */
10980     waitMsgTarget : false,
10981
10982     loadMask : true,
10983     
10984     /**
10985      * @cfg {Boolean} errorMask (true|false) default false
10986      */
10987     errorMask : false,
10988     
10989     /**
10990      * @cfg {Number} maskOffset Default 100
10991      */
10992     maskOffset : 100,
10993     
10994     /**
10995      * @cfg {Boolean} maskBody
10996      */
10997     maskBody : false,
10998
10999     getAutoCreate : function(){
11000
11001         var cfg = {
11002             tag: 'form',
11003             method : this.method || 'POST',
11004             id : this.id || Roo.id(),
11005             cls : ''
11006         };
11007         if (this.parent().xtype.match(/^Nav/)) {
11008             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11009
11010         }
11011
11012         if (this.labelAlign == 'left' ) {
11013             cfg.cls += ' form-horizontal';
11014         }
11015
11016
11017         return cfg;
11018     },
11019     initEvents : function()
11020     {
11021         this.el.on('submit', this.onSubmit, this);
11022         // this was added as random key presses on the form where triggering form submit.
11023         this.el.on('keypress', function(e) {
11024             if (e.getCharCode() != 13) {
11025                 return true;
11026             }
11027             // we might need to allow it for textareas.. and some other items.
11028             // check e.getTarget().
11029
11030             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11031                 return true;
11032             }
11033
11034             Roo.log("keypress blocked");
11035
11036             e.preventDefault();
11037             return false;
11038         });
11039         
11040     },
11041     // private
11042     onSubmit : function(e){
11043         e.stopEvent();
11044     },
11045
11046      /**
11047      * Returns true if client-side validation on the form is successful.
11048      * @return Boolean
11049      */
11050     isValid : function(){
11051         var items = this.getItems();
11052         var valid = true;
11053         var target = false;
11054         
11055         items.each(function(f){
11056             
11057             if(f.validate()){
11058                 return;
11059             }
11060             
11061             Roo.log('invalid field: ' + f.name);
11062             
11063             valid = false;
11064
11065             if(!target && f.el.isVisible(true)){
11066                 target = f;
11067             }
11068            
11069         });
11070         
11071         if(this.errorMask && !valid){
11072             Roo.bootstrap.Form.popover.mask(this, target);
11073         }
11074         
11075         return valid;
11076     },
11077     
11078     /**
11079      * Returns true if any fields in this form have changed since their original load.
11080      * @return Boolean
11081      */
11082     isDirty : function(){
11083         var dirty = false;
11084         var items = this.getItems();
11085         items.each(function(f){
11086            if(f.isDirty()){
11087                dirty = true;
11088                return false;
11089            }
11090            return true;
11091         });
11092         return dirty;
11093     },
11094      /**
11095      * Performs a predefined action (submit or load) or custom actions you define on this form.
11096      * @param {String} actionName The name of the action type
11097      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11098      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11099      * accept other config options):
11100      * <pre>
11101 Property          Type             Description
11102 ----------------  ---------------  ----------------------------------------------------------------------------------
11103 url               String           The url for the action (defaults to the form's url)
11104 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11105 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11106 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11107                                    validate the form on the client (defaults to false)
11108      * </pre>
11109      * @return {BasicForm} this
11110      */
11111     doAction : function(action, options){
11112         if(typeof action == 'string'){
11113             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11114         }
11115         if(this.fireEvent('beforeaction', this, action) !== false){
11116             this.beforeAction(action);
11117             action.run.defer(100, action);
11118         }
11119         return this;
11120     },
11121
11122     // private
11123     beforeAction : function(action){
11124         var o = action.options;
11125         
11126         if(this.loadMask){
11127             
11128             if(this.maskBody){
11129                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11130             } else {
11131                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11132             }
11133         }
11134         // not really supported yet.. ??
11135
11136         //if(this.waitMsgTarget === true){
11137         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11138         //}else if(this.waitMsgTarget){
11139         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11140         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11141         //}else {
11142         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11143        // }
11144
11145     },
11146
11147     // private
11148     afterAction : function(action, success){
11149         this.activeAction = null;
11150         var o = action.options;
11151
11152         if(this.loadMask){
11153             
11154             if(this.maskBody){
11155                 Roo.get(document.body).unmask();
11156             } else {
11157                 this.el.unmask();
11158             }
11159         }
11160         
11161         //if(this.waitMsgTarget === true){
11162 //            this.el.unmask();
11163         //}else if(this.waitMsgTarget){
11164         //    this.waitMsgTarget.unmask();
11165         //}else{
11166         //    Roo.MessageBox.updateProgress(1);
11167         //    Roo.MessageBox.hide();
11168        // }
11169         //
11170         if(success){
11171             if(o.reset){
11172                 this.reset();
11173             }
11174             Roo.callback(o.success, o.scope, [this, action]);
11175             this.fireEvent('actioncomplete', this, action);
11176
11177         }else{
11178
11179             // failure condition..
11180             // we have a scenario where updates need confirming.
11181             // eg. if a locking scenario exists..
11182             // we look for { errors : { needs_confirm : true }} in the response.
11183             if (
11184                 (typeof(action.result) != 'undefined')  &&
11185                 (typeof(action.result.errors) != 'undefined')  &&
11186                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11187            ){
11188                 var _t = this;
11189                 Roo.log("not supported yet");
11190                  /*
11191
11192                 Roo.MessageBox.confirm(
11193                     "Change requires confirmation",
11194                     action.result.errorMsg,
11195                     function(r) {
11196                         if (r != 'yes') {
11197                             return;
11198                         }
11199                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11200                     }
11201
11202                 );
11203                 */
11204
11205
11206                 return;
11207             }
11208
11209             Roo.callback(o.failure, o.scope, [this, action]);
11210             // show an error message if no failed handler is set..
11211             if (!this.hasListener('actionfailed')) {
11212                 Roo.log("need to add dialog support");
11213                 /*
11214                 Roo.MessageBox.alert("Error",
11215                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11216                         action.result.errorMsg :
11217                         "Saving Failed, please check your entries or try again"
11218                 );
11219                 */
11220             }
11221
11222             this.fireEvent('actionfailed', this, action);
11223         }
11224
11225     },
11226     /**
11227      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11228      * @param {String} id The value to search for
11229      * @return Field
11230      */
11231     findField : function(id){
11232         var items = this.getItems();
11233         var field = items.get(id);
11234         if(!field){
11235              items.each(function(f){
11236                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11237                     field = f;
11238                     return false;
11239                 }
11240                 return true;
11241             });
11242         }
11243         return field || null;
11244     },
11245      /**
11246      * Mark fields in this form invalid in bulk.
11247      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11248      * @return {BasicForm} this
11249      */
11250     markInvalid : function(errors){
11251         if(errors instanceof Array){
11252             for(var i = 0, len = errors.length; i < len; i++){
11253                 var fieldError = errors[i];
11254                 var f = this.findField(fieldError.id);
11255                 if(f){
11256                     f.markInvalid(fieldError.msg);
11257                 }
11258             }
11259         }else{
11260             var field, id;
11261             for(id in errors){
11262                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11263                     field.markInvalid(errors[id]);
11264                 }
11265             }
11266         }
11267         //Roo.each(this.childForms || [], function (f) {
11268         //    f.markInvalid(errors);
11269         //});
11270
11271         return this;
11272     },
11273
11274     /**
11275      * Set values for fields in this form in bulk.
11276      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11277      * @return {BasicForm} this
11278      */
11279     setValues : function(values){
11280         if(values instanceof Array){ // array of objects
11281             for(var i = 0, len = values.length; i < len; i++){
11282                 var v = values[i];
11283                 var f = this.findField(v.id);
11284                 if(f){
11285                     f.setValue(v.value);
11286                     if(this.trackResetOnLoad){
11287                         f.originalValue = f.getValue();
11288                     }
11289                 }
11290             }
11291         }else{ // object hash
11292             var field, id;
11293             for(id in values){
11294                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11295
11296                     if (field.setFromData &&
11297                         field.valueField &&
11298                         field.displayField &&
11299                         // combos' with local stores can
11300                         // be queried via setValue()
11301                         // to set their value..
11302                         (field.store && !field.store.isLocal)
11303                         ) {
11304                         // it's a combo
11305                         var sd = { };
11306                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11307                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11308                         field.setFromData(sd);
11309
11310                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11311                         
11312                         field.setFromData(values);
11313                         
11314                     } else {
11315                         field.setValue(values[id]);
11316                     }
11317
11318
11319                     if(this.trackResetOnLoad){
11320                         field.originalValue = field.getValue();
11321                     }
11322                 }
11323             }
11324         }
11325
11326         //Roo.each(this.childForms || [], function (f) {
11327         //    f.setValues(values);
11328         //});
11329
11330         return this;
11331     },
11332
11333     /**
11334      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11335      * they are returned as an array.
11336      * @param {Boolean} asString
11337      * @return {Object}
11338      */
11339     getValues : function(asString){
11340         //if (this.childForms) {
11341             // copy values from the child forms
11342         //    Roo.each(this.childForms, function (f) {
11343         //        this.setValues(f.getValues());
11344         //    }, this);
11345         //}
11346
11347
11348
11349         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11350         if(asString === true){
11351             return fs;
11352         }
11353         return Roo.urlDecode(fs);
11354     },
11355
11356     /**
11357      * Returns the fields in this form as an object with key/value pairs.
11358      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11359      * @return {Object}
11360      */
11361     getFieldValues : function(with_hidden)
11362     {
11363         var items = this.getItems();
11364         var ret = {};
11365         items.each(function(f){
11366             
11367             if (!f.getName()) {
11368                 return;
11369             }
11370             
11371             var v = f.getValue();
11372             
11373             if (f.inputType =='radio') {
11374                 if (typeof(ret[f.getName()]) == 'undefined') {
11375                     ret[f.getName()] = ''; // empty..
11376                 }
11377
11378                 if (!f.el.dom.checked) {
11379                     return;
11380
11381                 }
11382                 v = f.el.dom.value;
11383
11384             }
11385             
11386             if(f.xtype == 'MoneyField'){
11387                 ret[f.currencyName] = f.getCurrency();
11388             }
11389
11390             // not sure if this supported any more..
11391             if ((typeof(v) == 'object') && f.getRawValue) {
11392                 v = f.getRawValue() ; // dates..
11393             }
11394             // combo boxes where name != hiddenName...
11395             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11396                 ret[f.name] = f.getRawValue();
11397             }
11398             ret[f.getName()] = v;
11399         });
11400
11401         return ret;
11402     },
11403
11404     /**
11405      * Clears all invalid messages in this form.
11406      * @return {BasicForm} this
11407      */
11408     clearInvalid : function(){
11409         var items = this.getItems();
11410
11411         items.each(function(f){
11412            f.clearInvalid();
11413         });
11414
11415         return this;
11416     },
11417
11418     /**
11419      * Resets this form.
11420      * @return {BasicForm} this
11421      */
11422     reset : function(){
11423         var items = this.getItems();
11424         items.each(function(f){
11425             f.reset();
11426         });
11427
11428         Roo.each(this.childForms || [], function (f) {
11429             f.reset();
11430         });
11431
11432
11433         return this;
11434     },
11435     
11436     getItems : function()
11437     {
11438         var r=new Roo.util.MixedCollection(false, function(o){
11439             return o.id || (o.id = Roo.id());
11440         });
11441         var iter = function(el) {
11442             if (el.inputEl) {
11443                 r.add(el);
11444             }
11445             if (!el.items) {
11446                 return;
11447             }
11448             Roo.each(el.items,function(e) {
11449                 iter(e);
11450             });
11451         };
11452
11453         iter(this);
11454         return r;
11455     },
11456     
11457     hideFields : function(items)
11458     {
11459         Roo.each(items, function(i){
11460             
11461             var f = this.findField(i);
11462             
11463             if(!f){
11464                 return;
11465             }
11466             
11467             f.hide();
11468             
11469         }, this);
11470     },
11471     
11472     showFields : function(items)
11473     {
11474         Roo.each(items, function(i){
11475             
11476             var f = this.findField(i);
11477             
11478             if(!f){
11479                 return;
11480             }
11481             
11482             f.show();
11483             
11484         }, this);
11485     }
11486
11487 });
11488
11489 Roo.apply(Roo.bootstrap.Form, {
11490     
11491     popover : {
11492         
11493         padding : 5,
11494         
11495         isApplied : false,
11496         
11497         isMasked : false,
11498         
11499         form : false,
11500         
11501         target : false,
11502         
11503         toolTip : false,
11504         
11505         intervalID : false,
11506         
11507         maskEl : false,
11508         
11509         apply : function()
11510         {
11511             if(this.isApplied){
11512                 return;
11513             }
11514             
11515             this.maskEl = {
11516                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11517                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11518                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11519                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11520             };
11521             
11522             this.maskEl.top.enableDisplayMode("block");
11523             this.maskEl.left.enableDisplayMode("block");
11524             this.maskEl.bottom.enableDisplayMode("block");
11525             this.maskEl.right.enableDisplayMode("block");
11526             
11527             this.toolTip = new Roo.bootstrap.Tooltip({
11528                 cls : 'roo-form-error-popover',
11529                 alignment : {
11530                     'left' : ['r-l', [-2,0], 'right'],
11531                     'right' : ['l-r', [2,0], 'left'],
11532                     'bottom' : ['tl-bl', [0,2], 'top'],
11533                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11534                 }
11535             });
11536             
11537             this.toolTip.render(Roo.get(document.body));
11538
11539             this.toolTip.el.enableDisplayMode("block");
11540             
11541             Roo.get(document.body).on('click', function(){
11542                 this.unmask();
11543             }, this);
11544             
11545             Roo.get(document.body).on('touchstart', function(){
11546                 this.unmask();
11547             }, this);
11548             
11549             this.isApplied = true
11550         },
11551         
11552         mask : function(form, target)
11553         {
11554             this.form = form;
11555             
11556             this.target = target;
11557             
11558             if(!this.form.errorMask || !target.el){
11559                 return;
11560             }
11561             
11562             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11563             
11564             Roo.log(scrollable);
11565             
11566             var ot = this.target.el.calcOffsetsTo(scrollable);
11567             
11568             var scrollTo = ot[1] - this.form.maskOffset;
11569             
11570             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11571             
11572             scrollable.scrollTo('top', scrollTo);
11573             
11574             var box = this.target.el.getBox();
11575             Roo.log(box);
11576             var zIndex = Roo.bootstrap.Modal.zIndex++;
11577
11578             
11579             this.maskEl.top.setStyle('position', 'absolute');
11580             this.maskEl.top.setStyle('z-index', zIndex);
11581             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11582             this.maskEl.top.setLeft(0);
11583             this.maskEl.top.setTop(0);
11584             this.maskEl.top.show();
11585             
11586             this.maskEl.left.setStyle('position', 'absolute');
11587             this.maskEl.left.setStyle('z-index', zIndex);
11588             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11589             this.maskEl.left.setLeft(0);
11590             this.maskEl.left.setTop(box.y - this.padding);
11591             this.maskEl.left.show();
11592
11593             this.maskEl.bottom.setStyle('position', 'absolute');
11594             this.maskEl.bottom.setStyle('z-index', zIndex);
11595             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11596             this.maskEl.bottom.setLeft(0);
11597             this.maskEl.bottom.setTop(box.bottom + this.padding);
11598             this.maskEl.bottom.show();
11599
11600             this.maskEl.right.setStyle('position', 'absolute');
11601             this.maskEl.right.setStyle('z-index', zIndex);
11602             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11603             this.maskEl.right.setLeft(box.right + this.padding);
11604             this.maskEl.right.setTop(box.y - this.padding);
11605             this.maskEl.right.show();
11606
11607             this.toolTip.bindEl = this.target.el;
11608
11609             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11610
11611             var tip = this.target.blankText;
11612
11613             if(this.target.getValue() !== '' ) {
11614                 
11615                 if (this.target.invalidText.length) {
11616                     tip = this.target.invalidText;
11617                 } else if (this.target.regexText.length){
11618                     tip = this.target.regexText;
11619                 }
11620             }
11621
11622             this.toolTip.show(tip);
11623
11624             this.intervalID = window.setInterval(function() {
11625                 Roo.bootstrap.Form.popover.unmask();
11626             }, 10000);
11627
11628             window.onwheel = function(){ return false;};
11629             
11630             (function(){ this.isMasked = true; }).defer(500, this);
11631             
11632         },
11633         
11634         unmask : function()
11635         {
11636             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11637                 return;
11638             }
11639             
11640             this.maskEl.top.setStyle('position', 'absolute');
11641             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11642             this.maskEl.top.hide();
11643
11644             this.maskEl.left.setStyle('position', 'absolute');
11645             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11646             this.maskEl.left.hide();
11647
11648             this.maskEl.bottom.setStyle('position', 'absolute');
11649             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11650             this.maskEl.bottom.hide();
11651
11652             this.maskEl.right.setStyle('position', 'absolute');
11653             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11654             this.maskEl.right.hide();
11655             
11656             this.toolTip.hide();
11657             
11658             this.toolTip.el.hide();
11659             
11660             window.onwheel = function(){ return true;};
11661             
11662             if(this.intervalID){
11663                 window.clearInterval(this.intervalID);
11664                 this.intervalID = false;
11665             }
11666             
11667             this.isMasked = false;
11668             
11669         }
11670         
11671     }
11672     
11673 });
11674
11675 /*
11676  * Based on:
11677  * Ext JS Library 1.1.1
11678  * Copyright(c) 2006-2007, Ext JS, LLC.
11679  *
11680  * Originally Released Under LGPL - original licence link has changed is not relivant.
11681  *
11682  * Fork - LGPL
11683  * <script type="text/javascript">
11684  */
11685 /**
11686  * @class Roo.form.VTypes
11687  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11688  * @singleton
11689  */
11690 Roo.form.VTypes = function(){
11691     // closure these in so they are only created once.
11692     var alpha = /^[a-zA-Z_]+$/;
11693     var alphanum = /^[a-zA-Z0-9_]+$/;
11694     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11695     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11696
11697     // All these messages and functions are configurable
11698     return {
11699         /**
11700          * The function used to validate email addresses
11701          * @param {String} value The email address
11702          */
11703         'email' : function(v){
11704             return email.test(v);
11705         },
11706         /**
11707          * The error text to display when the email validation function returns false
11708          * @type String
11709          */
11710         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11711         /**
11712          * The keystroke filter mask to be applied on email input
11713          * @type RegExp
11714          */
11715         'emailMask' : /[a-z0-9_\.\-@]/i,
11716
11717         /**
11718          * The function used to validate URLs
11719          * @param {String} value The URL
11720          */
11721         'url' : function(v){
11722             return url.test(v);
11723         },
11724         /**
11725          * The error text to display when the url validation function returns false
11726          * @type String
11727          */
11728         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11729         
11730         /**
11731          * The function used to validate alpha values
11732          * @param {String} value The value
11733          */
11734         'alpha' : function(v){
11735             return alpha.test(v);
11736         },
11737         /**
11738          * The error text to display when the alpha validation function returns false
11739          * @type String
11740          */
11741         'alphaText' : 'This field should only contain letters and _',
11742         /**
11743          * The keystroke filter mask to be applied on alpha input
11744          * @type RegExp
11745          */
11746         'alphaMask' : /[a-z_]/i,
11747
11748         /**
11749          * The function used to validate alphanumeric values
11750          * @param {String} value The value
11751          */
11752         'alphanum' : function(v){
11753             return alphanum.test(v);
11754         },
11755         /**
11756          * The error text to display when the alphanumeric validation function returns false
11757          * @type String
11758          */
11759         'alphanumText' : 'This field should only contain letters, numbers and _',
11760         /**
11761          * The keystroke filter mask to be applied on alphanumeric input
11762          * @type RegExp
11763          */
11764         'alphanumMask' : /[a-z0-9_]/i
11765     };
11766 }();/*
11767  * - LGPL
11768  *
11769  * Input
11770  * 
11771  */
11772
11773 /**
11774  * @class Roo.bootstrap.Input
11775  * @extends Roo.bootstrap.Component
11776  * Bootstrap Input class
11777  * @cfg {Boolean} disabled is it disabled
11778  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11779  * @cfg {String} name name of the input
11780  * @cfg {string} fieldLabel - the label associated
11781  * @cfg {string} placeholder - placeholder to put in text.
11782  * @cfg {string}  before - input group add on before
11783  * @cfg {string} after - input group add on after
11784  * @cfg {string} size - (lg|sm) or leave empty..
11785  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11786  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11787  * @cfg {Number} md colspan out of 12 for computer-sized screens
11788  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11789  * @cfg {string} value default value of the input
11790  * @cfg {Number} labelWidth set the width of label 
11791  * @cfg {Number} labellg set the width of label (1-12)
11792  * @cfg {Number} labelmd set the width of label (1-12)
11793  * @cfg {Number} labelsm set the width of label (1-12)
11794  * @cfg {Number} labelxs set the width of label (1-12)
11795  * @cfg {String} labelAlign (top|left)
11796  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11797  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11798  * @cfg {String} indicatorpos (left|right) default left
11799  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11800  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11801  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11802
11803  * @cfg {String} align (left|center|right) Default left
11804  * @cfg {Boolean} forceFeedback (true|false) Default false
11805  * 
11806  * @constructor
11807  * Create a new Input
11808  * @param {Object} config The config object
11809  */
11810
11811 Roo.bootstrap.Input = function(config){
11812     
11813     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11814     
11815     this.addEvents({
11816         /**
11817          * @event focus
11818          * Fires when this field receives input focus.
11819          * @param {Roo.form.Field} this
11820          */
11821         focus : true,
11822         /**
11823          * @event blur
11824          * Fires when this field loses input focus.
11825          * @param {Roo.form.Field} this
11826          */
11827         blur : true,
11828         /**
11829          * @event specialkey
11830          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11831          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11832          * @param {Roo.form.Field} this
11833          * @param {Roo.EventObject} e The event object
11834          */
11835         specialkey : true,
11836         /**
11837          * @event change
11838          * Fires just before the field blurs if the field value has changed.
11839          * @param {Roo.form.Field} this
11840          * @param {Mixed} newValue The new value
11841          * @param {Mixed} oldValue The original value
11842          */
11843         change : true,
11844         /**
11845          * @event invalid
11846          * Fires after the field has been marked as invalid.
11847          * @param {Roo.form.Field} this
11848          * @param {String} msg The validation message
11849          */
11850         invalid : true,
11851         /**
11852          * @event valid
11853          * Fires after the field has been validated with no errors.
11854          * @param {Roo.form.Field} this
11855          */
11856         valid : true,
11857          /**
11858          * @event keyup
11859          * Fires after the key up
11860          * @param {Roo.form.Field} this
11861          * @param {Roo.EventObject}  e The event Object
11862          */
11863         keyup : true,
11864         /**
11865          * @event paste
11866          * Fires after the user pastes into input
11867          * @param {Roo.form.Field} this
11868          * @param {Roo.EventObject}  e The event Object
11869          */
11870         paste : true
11871     });
11872 };
11873
11874 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11875      /**
11876      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11877       automatic validation (defaults to "keyup").
11878      */
11879     validationEvent : "keyup",
11880      /**
11881      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11882      */
11883     validateOnBlur : true,
11884     /**
11885      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11886      */
11887     validationDelay : 250,
11888      /**
11889      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11890      */
11891     focusClass : "x-form-focus",  // not needed???
11892     
11893        
11894     /**
11895      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11896      */
11897     invalidClass : "has-warning",
11898     
11899     /**
11900      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11901      */
11902     validClass : "has-success",
11903     
11904     /**
11905      * @cfg {Boolean} hasFeedback (true|false) default true
11906      */
11907     hasFeedback : true,
11908     
11909     /**
11910      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11911      */
11912     invalidFeedbackClass : "glyphicon-warning-sign",
11913     
11914     /**
11915      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11916      */
11917     validFeedbackClass : "glyphicon-ok",
11918     
11919     /**
11920      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11921      */
11922     selectOnFocus : false,
11923     
11924      /**
11925      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11926      */
11927     maskRe : null,
11928        /**
11929      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11930      */
11931     vtype : null,
11932     
11933       /**
11934      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11935      */
11936     disableKeyFilter : false,
11937     
11938        /**
11939      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11940      */
11941     disabled : false,
11942      /**
11943      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11944      */
11945     allowBlank : true,
11946     /**
11947      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11948      */
11949     blankText : "Please complete this mandatory field",
11950     
11951      /**
11952      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11953      */
11954     minLength : 0,
11955     /**
11956      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11957      */
11958     maxLength : Number.MAX_VALUE,
11959     /**
11960      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11961      */
11962     minLengthText : "The minimum length for this field is {0}",
11963     /**
11964      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11965      */
11966     maxLengthText : "The maximum length for this field is {0}",
11967   
11968     
11969     /**
11970      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11971      * If available, this function will be called only after the basic validators all return true, and will be passed the
11972      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11973      */
11974     validator : null,
11975     /**
11976      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11977      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11978      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11979      */
11980     regex : null,
11981     /**
11982      * @cfg {String} regexText -- Depricated - use Invalid Text
11983      */
11984     regexText : "",
11985     
11986     /**
11987      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11988      */
11989     invalidText : "",
11990     
11991     
11992     
11993     autocomplete: false,
11994     
11995     
11996     fieldLabel : '',
11997     inputType : 'text',
11998     
11999     name : false,
12000     placeholder: false,
12001     before : false,
12002     after : false,
12003     size : false,
12004     hasFocus : false,
12005     preventMark: false,
12006     isFormField : true,
12007     value : '',
12008     labelWidth : 2,
12009     labelAlign : false,
12010     readOnly : false,
12011     align : false,
12012     formatedValue : false,
12013     forceFeedback : false,
12014     
12015     indicatorpos : 'left',
12016     
12017     labellg : 0,
12018     labelmd : 0,
12019     labelsm : 0,
12020     labelxs : 0,
12021     
12022     capture : '',
12023     accept : '',
12024     
12025     parentLabelAlign : function()
12026     {
12027         var parent = this;
12028         while (parent.parent()) {
12029             parent = parent.parent();
12030             if (typeof(parent.labelAlign) !='undefined') {
12031                 return parent.labelAlign;
12032             }
12033         }
12034         return 'left';
12035         
12036     },
12037     
12038     getAutoCreate : function()
12039     {
12040         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12041         
12042         var id = Roo.id();
12043         
12044         var cfg = {};
12045         
12046         if(this.inputType != 'hidden'){
12047             cfg.cls = 'form-group' //input-group
12048         }
12049         
12050         var input =  {
12051             tag: 'input',
12052             id : id,
12053             type : this.inputType,
12054             value : this.value,
12055             cls : 'form-control',
12056             placeholder : this.placeholder || '',
12057             autocomplete : this.autocomplete || 'new-password'
12058         };
12059         if (this.inputType == 'file') {
12060             input.style = 'overflow:hidden'; // why not in CSS?
12061         }
12062         
12063         if(this.capture.length){
12064             input.capture = this.capture;
12065         }
12066         
12067         if(this.accept.length){
12068             input.accept = this.accept + "/*";
12069         }
12070         
12071         if(this.align){
12072             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12073         }
12074         
12075         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12076             input.maxLength = this.maxLength;
12077         }
12078         
12079         if (this.disabled) {
12080             input.disabled=true;
12081         }
12082         
12083         if (this.readOnly) {
12084             input.readonly=true;
12085         }
12086         
12087         if (this.name) {
12088             input.name = this.name;
12089         }
12090         
12091         if (this.size) {
12092             input.cls += ' input-' + this.size;
12093         }
12094         
12095         var settings=this;
12096         ['xs','sm','md','lg'].map(function(size){
12097             if (settings[size]) {
12098                 cfg.cls += ' col-' + size + '-' + settings[size];
12099             }
12100         });
12101         
12102         var inputblock = input;
12103         
12104         var feedback = {
12105             tag: 'span',
12106             cls: 'glyphicon form-control-feedback'
12107         };
12108             
12109         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12110             
12111             inputblock = {
12112                 cls : 'has-feedback',
12113                 cn :  [
12114                     input,
12115                     feedback
12116                 ] 
12117             };  
12118         }
12119         
12120         if (this.before || this.after) {
12121             
12122             inputblock = {
12123                 cls : 'input-group',
12124                 cn :  [] 
12125             };
12126             
12127             if (this.before && typeof(this.before) == 'string') {
12128                 
12129                 inputblock.cn.push({
12130                     tag :'span',
12131                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12132                     html : this.before
12133                 });
12134             }
12135             if (this.before && typeof(this.before) == 'object') {
12136                 this.before = Roo.factory(this.before);
12137                 
12138                 inputblock.cn.push({
12139                     tag :'span',
12140                     cls : 'roo-input-before input-group-prepend   input-group-' +
12141                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12142                 });
12143             }
12144             
12145             inputblock.cn.push(input);
12146             
12147             if (this.after && typeof(this.after) == 'string') {
12148                 inputblock.cn.push({
12149                     tag :'span',
12150                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12151                     html : this.after
12152                 });
12153             }
12154             if (this.after && typeof(this.after) == 'object') {
12155                 this.after = Roo.factory(this.after);
12156                 
12157                 inputblock.cn.push({
12158                     tag :'span',
12159                     cls : 'roo-input-after input-group-append  input-group-' +
12160                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12161                 });
12162             }
12163             
12164             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12165                 inputblock.cls += ' has-feedback';
12166                 inputblock.cn.push(feedback);
12167             }
12168         };
12169         var indicator = {
12170             tag : 'i',
12171             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12172             tooltip : 'This field is required'
12173         };
12174         if (this.allowBlank ) {
12175             indicator.style = this.allowBlank ? ' display:none' : '';
12176         }
12177         if (align ==='left' && this.fieldLabel.length) {
12178             
12179             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12180             
12181             cfg.cn = [
12182                 indicator,
12183                 {
12184                     tag: 'label',
12185                     'for' :  id,
12186                     cls : 'control-label col-form-label',
12187                     html : this.fieldLabel
12188
12189                 },
12190                 {
12191                     cls : "", 
12192                     cn: [
12193                         inputblock
12194                     ]
12195                 }
12196             ];
12197             
12198             var labelCfg = cfg.cn[1];
12199             var contentCfg = cfg.cn[2];
12200             
12201             if(this.indicatorpos == 'right'){
12202                 cfg.cn = [
12203                     {
12204                         tag: 'label',
12205                         'for' :  id,
12206                         cls : 'control-label col-form-label',
12207                         cn : [
12208                             {
12209                                 tag : 'span',
12210                                 html : this.fieldLabel
12211                             },
12212                             indicator
12213                         ]
12214                     },
12215                     {
12216                         cls : "",
12217                         cn: [
12218                             inputblock
12219                         ]
12220                     }
12221
12222                 ];
12223                 
12224                 labelCfg = cfg.cn[0];
12225                 contentCfg = cfg.cn[1];
12226             
12227             }
12228             
12229             if(this.labelWidth > 12){
12230                 labelCfg.style = "width: " + this.labelWidth + 'px';
12231             }
12232             
12233             if(this.labelWidth < 13 && this.labelmd == 0){
12234                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12235             }
12236             
12237             if(this.labellg > 0){
12238                 labelCfg.cls += ' col-lg-' + this.labellg;
12239                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12240             }
12241             
12242             if(this.labelmd > 0){
12243                 labelCfg.cls += ' col-md-' + this.labelmd;
12244                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12245             }
12246             
12247             if(this.labelsm > 0){
12248                 labelCfg.cls += ' col-sm-' + this.labelsm;
12249                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12250             }
12251             
12252             if(this.labelxs > 0){
12253                 labelCfg.cls += ' col-xs-' + this.labelxs;
12254                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12255             }
12256             
12257             
12258         } else if ( this.fieldLabel.length) {
12259                 
12260             
12261             
12262             cfg.cn = [
12263                 {
12264                     tag : 'i',
12265                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12266                     tooltip : 'This field is required',
12267                     style : this.allowBlank ? ' display:none' : '' 
12268                 },
12269                 {
12270                     tag: 'label',
12271                    //cls : 'input-group-addon',
12272                     html : this.fieldLabel
12273
12274                 },
12275
12276                inputblock
12277
12278            ];
12279            
12280            if(this.indicatorpos == 'right'){
12281        
12282                 cfg.cn = [
12283                     {
12284                         tag: 'label',
12285                        //cls : 'input-group-addon',
12286                         html : this.fieldLabel
12287
12288                     },
12289                     {
12290                         tag : 'i',
12291                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12292                         tooltip : 'This field is required',
12293                         style : this.allowBlank ? ' display:none' : '' 
12294                     },
12295
12296                    inputblock
12297
12298                ];
12299
12300             }
12301
12302         } else {
12303             
12304             cfg.cn = [
12305
12306                     inputblock
12307
12308             ];
12309                 
12310                 
12311         };
12312         
12313         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12314            cfg.cls += ' navbar-form';
12315         }
12316         
12317         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12318             // on BS4 we do this only if not form 
12319             cfg.cls += ' navbar-form';
12320             cfg.tag = 'li';
12321         }
12322         
12323         return cfg;
12324         
12325     },
12326     /**
12327      * return the real input element.
12328      */
12329     inputEl: function ()
12330     {
12331         return this.el.select('input.form-control',true).first();
12332     },
12333     
12334     tooltipEl : function()
12335     {
12336         return this.inputEl();
12337     },
12338     
12339     indicatorEl : function()
12340     {
12341         if (Roo.bootstrap.version == 4) {
12342             return false; // not enabled in v4 yet.
12343         }
12344         
12345         var indicator = this.el.select('i.roo-required-indicator',true).first();
12346         
12347         if(!indicator){
12348             return false;
12349         }
12350         
12351         return indicator;
12352         
12353     },
12354     
12355     setDisabled : function(v)
12356     {
12357         var i  = this.inputEl().dom;
12358         if (!v) {
12359             i.removeAttribute('disabled');
12360             return;
12361             
12362         }
12363         i.setAttribute('disabled','true');
12364     },
12365     initEvents : function()
12366     {
12367           
12368         this.inputEl().on("keydown" , this.fireKey,  this);
12369         this.inputEl().on("focus", this.onFocus,  this);
12370         this.inputEl().on("blur", this.onBlur,  this);
12371         
12372         this.inputEl().relayEvent('keyup', this);
12373         this.inputEl().relayEvent('paste', this);
12374         
12375         this.indicator = this.indicatorEl();
12376         
12377         if(this.indicator){
12378             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12379         }
12380  
12381         // reference to original value for reset
12382         this.originalValue = this.getValue();
12383         //Roo.form.TextField.superclass.initEvents.call(this);
12384         if(this.validationEvent == 'keyup'){
12385             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12386             this.inputEl().on('keyup', this.filterValidation, this);
12387         }
12388         else if(this.validationEvent !== false){
12389             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12390         }
12391         
12392         if(this.selectOnFocus){
12393             this.on("focus", this.preFocus, this);
12394             
12395         }
12396         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12397             this.inputEl().on("keypress", this.filterKeys, this);
12398         } else {
12399             this.inputEl().relayEvent('keypress', this);
12400         }
12401        /* if(this.grow){
12402             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12403             this.el.on("click", this.autoSize,  this);
12404         }
12405         */
12406         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12407             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12408         }
12409         
12410         if (typeof(this.before) == 'object') {
12411             this.before.render(this.el.select('.roo-input-before',true).first());
12412         }
12413         if (typeof(this.after) == 'object') {
12414             this.after.render(this.el.select('.roo-input-after',true).first());
12415         }
12416         
12417         this.inputEl().on('change', this.onChange, this);
12418         
12419     },
12420     filterValidation : function(e){
12421         if(!e.isNavKeyPress()){
12422             this.validationTask.delay(this.validationDelay);
12423         }
12424     },
12425      /**
12426      * Validates the field value
12427      * @return {Boolean} True if the value is valid, else false
12428      */
12429     validate : function(){
12430         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12431         if(this.disabled || this.validateValue(this.getRawValue())){
12432             this.markValid();
12433             return true;
12434         }
12435         
12436         this.markInvalid();
12437         return false;
12438     },
12439     
12440     
12441     /**
12442      * Validates a value according to the field's validation rules and marks the field as invalid
12443      * if the validation fails
12444      * @param {Mixed} value The value to validate
12445      * @return {Boolean} True if the value is valid, else false
12446      */
12447     validateValue : function(value)
12448     {
12449         if(this.getVisibilityEl().hasClass('hidden')){
12450             return true;
12451         }
12452         
12453         if(value.length < 1)  { // if it's blank
12454             if(this.allowBlank){
12455                 return true;
12456             }
12457             return false;
12458         }
12459         
12460         if(value.length < this.minLength){
12461             return false;
12462         }
12463         if(value.length > this.maxLength){
12464             return false;
12465         }
12466         if(this.vtype){
12467             var vt = Roo.form.VTypes;
12468             if(!vt[this.vtype](value, this)){
12469                 return false;
12470             }
12471         }
12472         if(typeof this.validator == "function"){
12473             var msg = this.validator(value);
12474             if(msg !== true){
12475                 return false;
12476             }
12477             if (typeof(msg) == 'string') {
12478                 this.invalidText = msg;
12479             }
12480         }
12481         
12482         if(this.regex && !this.regex.test(value)){
12483             return false;
12484         }
12485         
12486         return true;
12487     },
12488     
12489      // private
12490     fireKey : function(e){
12491         //Roo.log('field ' + e.getKey());
12492         if(e.isNavKeyPress()){
12493             this.fireEvent("specialkey", this, e);
12494         }
12495     },
12496     focus : function (selectText){
12497         if(this.rendered){
12498             this.inputEl().focus();
12499             if(selectText === true){
12500                 this.inputEl().dom.select();
12501             }
12502         }
12503         return this;
12504     } ,
12505     
12506     onFocus : function(){
12507         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12508            // this.el.addClass(this.focusClass);
12509         }
12510         if(!this.hasFocus){
12511             this.hasFocus = true;
12512             this.startValue = this.getValue();
12513             this.fireEvent("focus", this);
12514         }
12515     },
12516     
12517     beforeBlur : Roo.emptyFn,
12518
12519     
12520     // private
12521     onBlur : function(){
12522         this.beforeBlur();
12523         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12524             //this.el.removeClass(this.focusClass);
12525         }
12526         this.hasFocus = false;
12527         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12528             this.validate();
12529         }
12530         var v = this.getValue();
12531         if(String(v) !== String(this.startValue)){
12532             this.fireEvent('change', this, v, this.startValue);
12533         }
12534         this.fireEvent("blur", this);
12535     },
12536     
12537     onChange : function(e)
12538     {
12539         var v = this.getValue();
12540         if(String(v) !== String(this.startValue)){
12541             this.fireEvent('change', this, v, this.startValue);
12542         }
12543         
12544     },
12545     
12546     /**
12547      * Resets the current field value to the originally loaded value and clears any validation messages
12548      */
12549     reset : function(){
12550         this.setValue(this.originalValue);
12551         this.validate();
12552     },
12553      /**
12554      * Returns the name of the field
12555      * @return {Mixed} name The name field
12556      */
12557     getName: function(){
12558         return this.name;
12559     },
12560      /**
12561      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12562      * @return {Mixed} value The field value
12563      */
12564     getValue : function(){
12565         
12566         var v = this.inputEl().getValue();
12567         
12568         return v;
12569     },
12570     /**
12571      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12572      * @return {Mixed} value The field value
12573      */
12574     getRawValue : function(){
12575         var v = this.inputEl().getValue();
12576         
12577         return v;
12578     },
12579     
12580     /**
12581      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12582      * @param {Mixed} value The value to set
12583      */
12584     setRawValue : function(v){
12585         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12586     },
12587     
12588     selectText : function(start, end){
12589         var v = this.getRawValue();
12590         if(v.length > 0){
12591             start = start === undefined ? 0 : start;
12592             end = end === undefined ? v.length : end;
12593             var d = this.inputEl().dom;
12594             if(d.setSelectionRange){
12595                 d.setSelectionRange(start, end);
12596             }else if(d.createTextRange){
12597                 var range = d.createTextRange();
12598                 range.moveStart("character", start);
12599                 range.moveEnd("character", v.length-end);
12600                 range.select();
12601             }
12602         }
12603     },
12604     
12605     /**
12606      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12607      * @param {Mixed} value The value to set
12608      */
12609     setValue : function(v){
12610         this.value = v;
12611         if(this.rendered){
12612             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12613             this.validate();
12614         }
12615     },
12616     
12617     /*
12618     processValue : function(value){
12619         if(this.stripCharsRe){
12620             var newValue = value.replace(this.stripCharsRe, '');
12621             if(newValue !== value){
12622                 this.setRawValue(newValue);
12623                 return newValue;
12624             }
12625         }
12626         return value;
12627     },
12628   */
12629     preFocus : function(){
12630         
12631         if(this.selectOnFocus){
12632             this.inputEl().dom.select();
12633         }
12634     },
12635     filterKeys : function(e){
12636         var k = e.getKey();
12637         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12638             return;
12639         }
12640         var c = e.getCharCode(), cc = String.fromCharCode(c);
12641         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12642             return;
12643         }
12644         if(!this.maskRe.test(cc)){
12645             e.stopEvent();
12646         }
12647     },
12648      /**
12649      * Clear any invalid styles/messages for this field
12650      */
12651     clearInvalid : function(){
12652         
12653         if(!this.el || this.preventMark){ // not rendered
12654             return;
12655         }
12656         
12657         
12658         this.el.removeClass([this.invalidClass, 'is-invalid']);
12659         
12660         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12661             
12662             var feedback = this.el.select('.form-control-feedback', true).first();
12663             
12664             if(feedback){
12665                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12666             }
12667             
12668         }
12669         
12670         if(this.indicator){
12671             this.indicator.removeClass('visible');
12672             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12673         }
12674         
12675         this.fireEvent('valid', this);
12676     },
12677     
12678      /**
12679      * Mark this field as valid
12680      */
12681     markValid : function()
12682     {
12683         if(!this.el  || this.preventMark){ // not rendered...
12684             return;
12685         }
12686         
12687         this.el.removeClass([this.invalidClass, this.validClass]);
12688         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12689
12690         var feedback = this.el.select('.form-control-feedback', true).first();
12691             
12692         if(feedback){
12693             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12694         }
12695         
12696         if(this.indicator){
12697             this.indicator.removeClass('visible');
12698             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12699         }
12700         
12701         if(this.disabled){
12702             return;
12703         }
12704         
12705            
12706         if(this.allowBlank && !this.getRawValue().length){
12707             return;
12708         }
12709         if (Roo.bootstrap.version == 3) {
12710             this.el.addClass(this.validClass);
12711         } else {
12712             this.inputEl().addClass('is-valid');
12713         }
12714
12715         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12716             
12717             var feedback = this.el.select('.form-control-feedback', true).first();
12718             
12719             if(feedback){
12720                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12721                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12722             }
12723             
12724         }
12725         
12726         this.fireEvent('valid', this);
12727     },
12728     
12729      /**
12730      * Mark this field as invalid
12731      * @param {String} msg The validation message
12732      */
12733     markInvalid : function(msg)
12734     {
12735         if(!this.el  || this.preventMark){ // not rendered
12736             return;
12737         }
12738         
12739         this.el.removeClass([this.invalidClass, this.validClass]);
12740         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12741         
12742         var feedback = this.el.select('.form-control-feedback', true).first();
12743             
12744         if(feedback){
12745             this.el.select('.form-control-feedback', true).first().removeClass(
12746                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12747         }
12748
12749         if(this.disabled){
12750             return;
12751         }
12752         
12753         if(this.allowBlank && !this.getRawValue().length){
12754             return;
12755         }
12756         
12757         if(this.indicator){
12758             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12759             this.indicator.addClass('visible');
12760         }
12761         if (Roo.bootstrap.version == 3) {
12762             this.el.addClass(this.invalidClass);
12763         } else {
12764             this.inputEl().addClass('is-invalid');
12765         }
12766         
12767         
12768         
12769         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12770             
12771             var feedback = this.el.select('.form-control-feedback', true).first();
12772             
12773             if(feedback){
12774                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12775                 
12776                 if(this.getValue().length || this.forceFeedback){
12777                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12778                 }
12779                 
12780             }
12781             
12782         }
12783         
12784         this.fireEvent('invalid', this, msg);
12785     },
12786     // private
12787     SafariOnKeyDown : function(event)
12788     {
12789         // this is a workaround for a password hang bug on chrome/ webkit.
12790         if (this.inputEl().dom.type != 'password') {
12791             return;
12792         }
12793         
12794         var isSelectAll = false;
12795         
12796         if(this.inputEl().dom.selectionEnd > 0){
12797             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12798         }
12799         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12800             event.preventDefault();
12801             this.setValue('');
12802             return;
12803         }
12804         
12805         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12806             
12807             event.preventDefault();
12808             // this is very hacky as keydown always get's upper case.
12809             //
12810             var cc = String.fromCharCode(event.getCharCode());
12811             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12812             
12813         }
12814     },
12815     adjustWidth : function(tag, w){
12816         tag = tag.toLowerCase();
12817         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12818             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12819                 if(tag == 'input'){
12820                     return w + 2;
12821                 }
12822                 if(tag == 'textarea'){
12823                     return w-2;
12824                 }
12825             }else if(Roo.isOpera){
12826                 if(tag == 'input'){
12827                     return w + 2;
12828                 }
12829                 if(tag == 'textarea'){
12830                     return w-2;
12831                 }
12832             }
12833         }
12834         return w;
12835     },
12836     
12837     setFieldLabel : function(v)
12838     {
12839         if(!this.rendered){
12840             return;
12841         }
12842         
12843         if(this.indicatorEl()){
12844             var ar = this.el.select('label > span',true);
12845             
12846             if (ar.elements.length) {
12847                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12848                 this.fieldLabel = v;
12849                 return;
12850             }
12851             
12852             var br = this.el.select('label',true);
12853             
12854             if(br.elements.length) {
12855                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12856                 this.fieldLabel = v;
12857                 return;
12858             }
12859             
12860             Roo.log('Cannot Found any of label > span || label in input');
12861             return;
12862         }
12863         
12864         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12865         this.fieldLabel = v;
12866         
12867         
12868     }
12869 });
12870
12871  
12872 /*
12873  * - LGPL
12874  *
12875  * Input
12876  * 
12877  */
12878
12879 /**
12880  * @class Roo.bootstrap.TextArea
12881  * @extends Roo.bootstrap.Input
12882  * Bootstrap TextArea class
12883  * @cfg {Number} cols Specifies the visible width of a text area
12884  * @cfg {Number} rows Specifies the visible number of lines in a text area
12885  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12886  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12887  * @cfg {string} html text
12888  * 
12889  * @constructor
12890  * Create a new TextArea
12891  * @param {Object} config The config object
12892  */
12893
12894 Roo.bootstrap.TextArea = function(config){
12895     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12896    
12897 };
12898
12899 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12900      
12901     cols : false,
12902     rows : 5,
12903     readOnly : false,
12904     warp : 'soft',
12905     resize : false,
12906     value: false,
12907     html: false,
12908     
12909     getAutoCreate : function(){
12910         
12911         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12912         
12913         var id = Roo.id();
12914         
12915         var cfg = {};
12916         
12917         if(this.inputType != 'hidden'){
12918             cfg.cls = 'form-group' //input-group
12919         }
12920         
12921         var input =  {
12922             tag: 'textarea',
12923             id : id,
12924             warp : this.warp,
12925             rows : this.rows,
12926             value : this.value || '',
12927             html: this.html || '',
12928             cls : 'form-control',
12929             placeholder : this.placeholder || '' 
12930             
12931         };
12932         
12933         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12934             input.maxLength = this.maxLength;
12935         }
12936         
12937         if(this.resize){
12938             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12939         }
12940         
12941         if(this.cols){
12942             input.cols = this.cols;
12943         }
12944         
12945         if (this.readOnly) {
12946             input.readonly = true;
12947         }
12948         
12949         if (this.name) {
12950             input.name = this.name;
12951         }
12952         
12953         if (this.size) {
12954             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12955         }
12956         
12957         var settings=this;
12958         ['xs','sm','md','lg'].map(function(size){
12959             if (settings[size]) {
12960                 cfg.cls += ' col-' + size + '-' + settings[size];
12961             }
12962         });
12963         
12964         var inputblock = input;
12965         
12966         if(this.hasFeedback && !this.allowBlank){
12967             
12968             var feedback = {
12969                 tag: 'span',
12970                 cls: 'glyphicon form-control-feedback'
12971             };
12972
12973             inputblock = {
12974                 cls : 'has-feedback',
12975                 cn :  [
12976                     input,
12977                     feedback
12978                 ] 
12979             };  
12980         }
12981         
12982         
12983         if (this.before || this.after) {
12984             
12985             inputblock = {
12986                 cls : 'input-group',
12987                 cn :  [] 
12988             };
12989             if (this.before) {
12990                 inputblock.cn.push({
12991                     tag :'span',
12992                     cls : 'input-group-addon',
12993                     html : this.before
12994                 });
12995             }
12996             
12997             inputblock.cn.push(input);
12998             
12999             if(this.hasFeedback && !this.allowBlank){
13000                 inputblock.cls += ' has-feedback';
13001                 inputblock.cn.push(feedback);
13002             }
13003             
13004             if (this.after) {
13005                 inputblock.cn.push({
13006                     tag :'span',
13007                     cls : 'input-group-addon',
13008                     html : this.after
13009                 });
13010             }
13011             
13012         }
13013         
13014         if (align ==='left' && this.fieldLabel.length) {
13015             cfg.cn = [
13016                 {
13017                     tag: 'label',
13018                     'for' :  id,
13019                     cls : 'control-label',
13020                     html : this.fieldLabel
13021                 },
13022                 {
13023                     cls : "",
13024                     cn: [
13025                         inputblock
13026                     ]
13027                 }
13028
13029             ];
13030             
13031             if(this.labelWidth > 12){
13032                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13033             }
13034
13035             if(this.labelWidth < 13 && this.labelmd == 0){
13036                 this.labelmd = this.labelWidth;
13037             }
13038
13039             if(this.labellg > 0){
13040                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13041                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13042             }
13043
13044             if(this.labelmd > 0){
13045                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13046                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13047             }
13048
13049             if(this.labelsm > 0){
13050                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13051                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13052             }
13053
13054             if(this.labelxs > 0){
13055                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13056                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13057             }
13058             
13059         } else if ( this.fieldLabel.length) {
13060             cfg.cn = [
13061
13062                {
13063                    tag: 'label',
13064                    //cls : 'input-group-addon',
13065                    html : this.fieldLabel
13066
13067                },
13068
13069                inputblock
13070
13071            ];
13072
13073         } else {
13074
13075             cfg.cn = [
13076
13077                 inputblock
13078
13079             ];
13080                 
13081         }
13082         
13083         if (this.disabled) {
13084             input.disabled=true;
13085         }
13086         
13087         return cfg;
13088         
13089     },
13090     /**
13091      * return the real textarea element.
13092      */
13093     inputEl: function ()
13094     {
13095         return this.el.select('textarea.form-control',true).first();
13096     },
13097     
13098     /**
13099      * Clear any invalid styles/messages for this field
13100      */
13101     clearInvalid : function()
13102     {
13103         
13104         if(!this.el || this.preventMark){ // not rendered
13105             return;
13106         }
13107         
13108         var label = this.el.select('label', true).first();
13109         var icon = this.el.select('i.fa-star', true).first();
13110         
13111         if(label && icon){
13112             icon.remove();
13113         }
13114         this.el.removeClass( this.validClass);
13115         this.inputEl().removeClass('is-invalid');
13116          
13117         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13118             
13119             var feedback = this.el.select('.form-control-feedback', true).first();
13120             
13121             if(feedback){
13122                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13123             }
13124             
13125         }
13126         
13127         this.fireEvent('valid', this);
13128     },
13129     
13130      /**
13131      * Mark this field as valid
13132      */
13133     markValid : function()
13134     {
13135         if(!this.el  || this.preventMark){ // not rendered
13136             return;
13137         }
13138         
13139         this.el.removeClass([this.invalidClass, this.validClass]);
13140         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13141         
13142         var feedback = this.el.select('.form-control-feedback', true).first();
13143             
13144         if(feedback){
13145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13146         }
13147
13148         if(this.disabled || this.allowBlank){
13149             return;
13150         }
13151         
13152         var label = this.el.select('label', true).first();
13153         var icon = this.el.select('i.fa-star', true).first();
13154         
13155         if(label && icon){
13156             icon.remove();
13157         }
13158         if (Roo.bootstrap.version == 3) {
13159             this.el.addClass(this.validClass);
13160         } else {
13161             this.inputEl().addClass('is-valid');
13162         }
13163         
13164         
13165         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13166             
13167             var feedback = this.el.select('.form-control-feedback', true).first();
13168             
13169             if(feedback){
13170                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13171                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13172             }
13173             
13174         }
13175         
13176         this.fireEvent('valid', this);
13177     },
13178     
13179      /**
13180      * Mark this field as invalid
13181      * @param {String} msg The validation message
13182      */
13183     markInvalid : function(msg)
13184     {
13185         if(!this.el  || this.preventMark){ // not rendered
13186             return;
13187         }
13188         
13189         this.el.removeClass([this.invalidClass, this.validClass]);
13190         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13191         
13192         var feedback = this.el.select('.form-control-feedback', true).first();
13193             
13194         if(feedback){
13195             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13196         }
13197
13198         if(this.disabled || this.allowBlank){
13199             return;
13200         }
13201         
13202         var label = this.el.select('label', true).first();
13203         var icon = this.el.select('i.fa-star', true).first();
13204         
13205         if(!this.getValue().length && label && !icon){
13206             this.el.createChild({
13207                 tag : 'i',
13208                 cls : 'text-danger fa fa-lg fa-star',
13209                 tooltip : 'This field is required',
13210                 style : 'margin-right:5px;'
13211             }, label, true);
13212         }
13213         
13214         if (Roo.bootstrap.version == 3) {
13215             this.el.addClass(this.invalidClass);
13216         } else {
13217             this.inputEl().addClass('is-invalid');
13218         }
13219         
13220         // fixme ... this may be depricated need to test..
13221         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13222             
13223             var feedback = this.el.select('.form-control-feedback', true).first();
13224             
13225             if(feedback){
13226                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13227                 
13228                 if(this.getValue().length || this.forceFeedback){
13229                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13230                 }
13231                 
13232             }
13233             
13234         }
13235         
13236         this.fireEvent('invalid', this, msg);
13237     }
13238 });
13239
13240  
13241 /*
13242  * - LGPL
13243  *
13244  * trigger field - base class for combo..
13245  * 
13246  */
13247  
13248 /**
13249  * @class Roo.bootstrap.TriggerField
13250  * @extends Roo.bootstrap.Input
13251  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13252  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13253  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13254  * for which you can provide a custom implementation.  For example:
13255  * <pre><code>
13256 var trigger = new Roo.bootstrap.TriggerField();
13257 trigger.onTriggerClick = myTriggerFn;
13258 trigger.applyTo('my-field');
13259 </code></pre>
13260  *
13261  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13262  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13263  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13264  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13265  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13266
13267  * @constructor
13268  * Create a new TriggerField.
13269  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13270  * to the base TextField)
13271  */
13272 Roo.bootstrap.TriggerField = function(config){
13273     this.mimicing = false;
13274     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13275 };
13276
13277 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13278     /**
13279      * @cfg {String} triggerClass A CSS class to apply to the trigger
13280      */
13281      /**
13282      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13283      */
13284     hideTrigger:false,
13285
13286     /**
13287      * @cfg {Boolean} removable (true|false) special filter default false
13288      */
13289     removable : false,
13290     
13291     /** @cfg {Boolean} grow @hide */
13292     /** @cfg {Number} growMin @hide */
13293     /** @cfg {Number} growMax @hide */
13294
13295     /**
13296      * @hide 
13297      * @method
13298      */
13299     autoSize: Roo.emptyFn,
13300     // private
13301     monitorTab : true,
13302     // private
13303     deferHeight : true,
13304
13305     
13306     actionMode : 'wrap',
13307     
13308     caret : false,
13309     
13310     
13311     getAutoCreate : function(){
13312        
13313         var align = this.labelAlign || this.parentLabelAlign();
13314         
13315         var id = Roo.id();
13316         
13317         var cfg = {
13318             cls: 'form-group' //input-group
13319         };
13320         
13321         
13322         var input =  {
13323             tag: 'input',
13324             id : id,
13325             type : this.inputType,
13326             cls : 'form-control',
13327             autocomplete: 'new-password',
13328             placeholder : this.placeholder || '' 
13329             
13330         };
13331         if (this.name) {
13332             input.name = this.name;
13333         }
13334         if (this.size) {
13335             input.cls += ' input-' + this.size;
13336         }
13337         
13338         if (this.disabled) {
13339             input.disabled=true;
13340         }
13341         
13342         var inputblock = input;
13343         
13344         if(this.hasFeedback && !this.allowBlank){
13345             
13346             var feedback = {
13347                 tag: 'span',
13348                 cls: 'glyphicon form-control-feedback'
13349             };
13350             
13351             if(this.removable && !this.editable  ){
13352                 inputblock = {
13353                     cls : 'has-feedback',
13354                     cn :  [
13355                         inputblock,
13356                         {
13357                             tag: 'button',
13358                             html : 'x',
13359                             cls : 'roo-combo-removable-btn close'
13360                         },
13361                         feedback
13362                     ] 
13363                 };
13364             } else {
13365                 inputblock = {
13366                     cls : 'has-feedback',
13367                     cn :  [
13368                         inputblock,
13369                         feedback
13370                     ] 
13371                 };
13372             }
13373
13374         } else {
13375             if(this.removable && !this.editable ){
13376                 inputblock = {
13377                     cls : 'roo-removable',
13378                     cn :  [
13379                         inputblock,
13380                         {
13381                             tag: 'button',
13382                             html : 'x',
13383                             cls : 'roo-combo-removable-btn close'
13384                         }
13385                     ] 
13386                 };
13387             }
13388         }
13389         
13390         if (this.before || this.after) {
13391             
13392             inputblock = {
13393                 cls : 'input-group',
13394                 cn :  [] 
13395             };
13396             if (this.before) {
13397                 inputblock.cn.push({
13398                     tag :'span',
13399                     cls : 'input-group-addon input-group-prepend input-group-text',
13400                     html : this.before
13401                 });
13402             }
13403             
13404             inputblock.cn.push(input);
13405             
13406             if(this.hasFeedback && !this.allowBlank){
13407                 inputblock.cls += ' has-feedback';
13408                 inputblock.cn.push(feedback);
13409             }
13410             
13411             if (this.after) {
13412                 inputblock.cn.push({
13413                     tag :'span',
13414                     cls : 'input-group-addon input-group-append input-group-text',
13415                     html : this.after
13416                 });
13417             }
13418             
13419         };
13420         
13421       
13422         
13423         var ibwrap = inputblock;
13424         
13425         if(this.multiple){
13426             ibwrap = {
13427                 tag: 'ul',
13428                 cls: 'roo-select2-choices',
13429                 cn:[
13430                     {
13431                         tag: 'li',
13432                         cls: 'roo-select2-search-field',
13433                         cn: [
13434
13435                             inputblock
13436                         ]
13437                     }
13438                 ]
13439             };
13440                 
13441         }
13442         
13443         var combobox = {
13444             cls: 'roo-select2-container input-group',
13445             cn: [
13446                  {
13447                     tag: 'input',
13448                     type : 'hidden',
13449                     cls: 'form-hidden-field'
13450                 },
13451                 ibwrap
13452             ]
13453         };
13454         
13455         if(!this.multiple && this.showToggleBtn){
13456             
13457             var caret = {
13458                         tag: 'span',
13459                         cls: 'caret'
13460              };
13461             if (this.caret != false) {
13462                 caret = {
13463                      tag: 'i',
13464                      cls: 'fa fa-' + this.caret
13465                 };
13466                 
13467             }
13468             
13469             combobox.cn.push({
13470                 tag :'span',
13471                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13472                 cn : [
13473                     Roo.bootstrap.version == 3 ? caret : '',
13474                     {
13475                         tag: 'span',
13476                         cls: 'combobox-clear',
13477                         cn  : [
13478                             {
13479                                 tag : 'i',
13480                                 cls: 'icon-remove'
13481                             }
13482                         ]
13483                     }
13484                 ]
13485
13486             })
13487         }
13488         
13489         if(this.multiple){
13490             combobox.cls += ' roo-select2-container-multi';
13491         }
13492          var indicator = {
13493             tag : 'i',
13494             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13495             tooltip : 'This field is required'
13496         };
13497         if (Roo.bootstrap.version == 4) {
13498             indicator = {
13499                 tag : 'i',
13500                 style : 'display:none'
13501             };
13502         }
13503         
13504         
13505         if (align ==='left' && this.fieldLabel.length) {
13506             
13507             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13508
13509             cfg.cn = [
13510                 indicator,
13511                 {
13512                     tag: 'label',
13513                     'for' :  id,
13514                     cls : 'control-label',
13515                     html : this.fieldLabel
13516
13517                 },
13518                 {
13519                     cls : "", 
13520                     cn: [
13521                         combobox
13522                     ]
13523                 }
13524
13525             ];
13526             
13527             var labelCfg = cfg.cn[1];
13528             var contentCfg = cfg.cn[2];
13529             
13530             if(this.indicatorpos == 'right'){
13531                 cfg.cn = [
13532                     {
13533                         tag: 'label',
13534                         'for' :  id,
13535                         cls : 'control-label',
13536                         cn : [
13537                             {
13538                                 tag : 'span',
13539                                 html : this.fieldLabel
13540                             },
13541                             indicator
13542                         ]
13543                     },
13544                     {
13545                         cls : "", 
13546                         cn: [
13547                             combobox
13548                         ]
13549                     }
13550
13551                 ];
13552                 
13553                 labelCfg = cfg.cn[0];
13554                 contentCfg = cfg.cn[1];
13555             }
13556             
13557             if(this.labelWidth > 12){
13558                 labelCfg.style = "width: " + this.labelWidth + 'px';
13559             }
13560             
13561             if(this.labelWidth < 13 && this.labelmd == 0){
13562                 this.labelmd = this.labelWidth;
13563             }
13564             
13565             if(this.labellg > 0){
13566                 labelCfg.cls += ' col-lg-' + this.labellg;
13567                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13568             }
13569             
13570             if(this.labelmd > 0){
13571                 labelCfg.cls += ' col-md-' + this.labelmd;
13572                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13573             }
13574             
13575             if(this.labelsm > 0){
13576                 labelCfg.cls += ' col-sm-' + this.labelsm;
13577                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13578             }
13579             
13580             if(this.labelxs > 0){
13581                 labelCfg.cls += ' col-xs-' + this.labelxs;
13582                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13583             }
13584             
13585         } else if ( this.fieldLabel.length) {
13586 //                Roo.log(" label");
13587             cfg.cn = [
13588                 indicator,
13589                {
13590                    tag: 'label',
13591                    //cls : 'input-group-addon',
13592                    html : this.fieldLabel
13593
13594                },
13595
13596                combobox
13597
13598             ];
13599             
13600             if(this.indicatorpos == 'right'){
13601                 
13602                 cfg.cn = [
13603                     {
13604                        tag: 'label',
13605                        cn : [
13606                            {
13607                                tag : 'span',
13608                                html : this.fieldLabel
13609                            },
13610                            indicator
13611                        ]
13612
13613                     },
13614                     combobox
13615
13616                 ];
13617
13618             }
13619
13620         } else {
13621             
13622 //                Roo.log(" no label && no align");
13623                 cfg = combobox
13624                      
13625                 
13626         }
13627         
13628         var settings=this;
13629         ['xs','sm','md','lg'].map(function(size){
13630             if (settings[size]) {
13631                 cfg.cls += ' col-' + size + '-' + settings[size];
13632             }
13633         });
13634         
13635         return cfg;
13636         
13637     },
13638     
13639     
13640     
13641     // private
13642     onResize : function(w, h){
13643 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13644 //        if(typeof w == 'number'){
13645 //            var x = w - this.trigger.getWidth();
13646 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13647 //            this.trigger.setStyle('left', x+'px');
13648 //        }
13649     },
13650
13651     // private
13652     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13653
13654     // private
13655     getResizeEl : function(){
13656         return this.inputEl();
13657     },
13658
13659     // private
13660     getPositionEl : function(){
13661         return this.inputEl();
13662     },
13663
13664     // private
13665     alignErrorIcon : function(){
13666         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13667     },
13668
13669     // private
13670     initEvents : function(){
13671         
13672         this.createList();
13673         
13674         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13675         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13676         if(!this.multiple && this.showToggleBtn){
13677             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13678             if(this.hideTrigger){
13679                 this.trigger.setDisplayed(false);
13680             }
13681             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13682         }
13683         
13684         if(this.multiple){
13685             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13686         }
13687         
13688         if(this.removable && !this.editable && !this.tickable){
13689             var close = this.closeTriggerEl();
13690             
13691             if(close){
13692                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13693                 close.on('click', this.removeBtnClick, this, close);
13694             }
13695         }
13696         
13697         //this.trigger.addClassOnOver('x-form-trigger-over');
13698         //this.trigger.addClassOnClick('x-form-trigger-click');
13699         
13700         //if(!this.width){
13701         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13702         //}
13703     },
13704     
13705     closeTriggerEl : function()
13706     {
13707         var close = this.el.select('.roo-combo-removable-btn', true).first();
13708         return close ? close : false;
13709     },
13710     
13711     removeBtnClick : function(e, h, el)
13712     {
13713         e.preventDefault();
13714         
13715         if(this.fireEvent("remove", this) !== false){
13716             this.reset();
13717             this.fireEvent("afterremove", this)
13718         }
13719     },
13720     
13721     createList : function()
13722     {
13723         this.list = Roo.get(document.body).createChild({
13724             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13725             cls: 'typeahead typeahead-long dropdown-menu shadow',
13726             style: 'display:none'
13727         });
13728         
13729         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13730         
13731     },
13732
13733     // private
13734     initTrigger : function(){
13735        
13736     },
13737
13738     // private
13739     onDestroy : function(){
13740         if(this.trigger){
13741             this.trigger.removeAllListeners();
13742           //  this.trigger.remove();
13743         }
13744         //if(this.wrap){
13745         //    this.wrap.remove();
13746         //}
13747         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13748     },
13749
13750     // private
13751     onFocus : function(){
13752         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13753         /*
13754         if(!this.mimicing){
13755             this.wrap.addClass('x-trigger-wrap-focus');
13756             this.mimicing = true;
13757             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13758             if(this.monitorTab){
13759                 this.el.on("keydown", this.checkTab, this);
13760             }
13761         }
13762         */
13763     },
13764
13765     // private
13766     checkTab : function(e){
13767         if(e.getKey() == e.TAB){
13768             this.triggerBlur();
13769         }
13770     },
13771
13772     // private
13773     onBlur : function(){
13774         // do nothing
13775     },
13776
13777     // private
13778     mimicBlur : function(e, t){
13779         /*
13780         if(!this.wrap.contains(t) && this.validateBlur()){
13781             this.triggerBlur();
13782         }
13783         */
13784     },
13785
13786     // private
13787     triggerBlur : function(){
13788         this.mimicing = false;
13789         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13790         if(this.monitorTab){
13791             this.el.un("keydown", this.checkTab, this);
13792         }
13793         //this.wrap.removeClass('x-trigger-wrap-focus');
13794         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13795     },
13796
13797     // private
13798     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13799     validateBlur : function(e, t){
13800         return true;
13801     },
13802
13803     // private
13804     onDisable : function(){
13805         this.inputEl().dom.disabled = true;
13806         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13807         //if(this.wrap){
13808         //    this.wrap.addClass('x-item-disabled');
13809         //}
13810     },
13811
13812     // private
13813     onEnable : function(){
13814         this.inputEl().dom.disabled = false;
13815         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13816         //if(this.wrap){
13817         //    this.el.removeClass('x-item-disabled');
13818         //}
13819     },
13820
13821     // private
13822     onShow : function(){
13823         var ae = this.getActionEl();
13824         
13825         if(ae){
13826             ae.dom.style.display = '';
13827             ae.dom.style.visibility = 'visible';
13828         }
13829     },
13830
13831     // private
13832     
13833     onHide : function(){
13834         var ae = this.getActionEl();
13835         ae.dom.style.display = 'none';
13836     },
13837
13838     /**
13839      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13840      * by an implementing function.
13841      * @method
13842      * @param {EventObject} e
13843      */
13844     onTriggerClick : Roo.emptyFn
13845 });
13846  
13847 /*
13848 * Licence: LGPL
13849 */
13850
13851 /**
13852  * @class Roo.bootstrap.CardUploader
13853  * @extends Roo.bootstrap.Button
13854  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13855  * @cfg {Number} errorTimeout default 3000
13856  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13857  * @cfg {Array}  html The button text.
13858
13859  *
13860  * @constructor
13861  * Create a new CardUploader
13862  * @param {Object} config The config object
13863  */
13864
13865 Roo.bootstrap.CardUploader = function(config){
13866     
13867  
13868     
13869     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13870     
13871     
13872     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13873         return r.data.id
13874      });
13875     
13876      this.addEvents({
13877          // raw events
13878         /**
13879          * @event preview
13880          * When a image is clicked on - and needs to display a slideshow or similar..
13881          * @param {Roo.bootstrap.Card} this
13882          * @param {Object} The image information data 
13883          *
13884          */
13885         'preview' : true,
13886          /**
13887          * @event download
13888          * When a the download link is clicked
13889          * @param {Roo.bootstrap.Card} this
13890          * @param {Object} The image information data  contains 
13891          */
13892         'download' : true
13893         
13894     });
13895 };
13896  
13897 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13898     
13899      
13900     errorTimeout : 3000,
13901      
13902     images : false,
13903    
13904     fileCollection : false,
13905     allowBlank : true,
13906     
13907     getAutoCreate : function()
13908     {
13909         
13910         var cfg =  {
13911             cls :'form-group' ,
13912             cn : [
13913                
13914                 {
13915                     tag: 'label',
13916                    //cls : 'input-group-addon',
13917                     html : this.fieldLabel
13918
13919                 },
13920
13921                 {
13922                     tag: 'input',
13923                     type : 'hidden',
13924                     name : this.name,
13925                     value : this.value,
13926                     cls : 'd-none  form-control'
13927                 },
13928                 
13929                 {
13930                     tag: 'input',
13931                     multiple : 'multiple',
13932                     type : 'file',
13933                     cls : 'd-none  roo-card-upload-selector'
13934                 },
13935                 
13936                 {
13937                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13938                 },
13939                 {
13940                     cls : 'card-columns roo-card-uploader-container'
13941                 }
13942
13943             ]
13944         };
13945            
13946          
13947         return cfg;
13948     },
13949     
13950     getChildContainer : function() /// what children are added to.
13951     {
13952         return this.containerEl;
13953     },
13954    
13955     getButtonContainer : function() /// what children are added to.
13956     {
13957         return this.el.select(".roo-card-uploader-button-container").first();
13958     },
13959    
13960     initEvents : function()
13961     {
13962         
13963         Roo.bootstrap.Input.prototype.initEvents.call(this);
13964         
13965         var t = this;
13966         this.addxtype({
13967             xns: Roo.bootstrap,
13968
13969             xtype : 'Button',
13970             container_method : 'getButtonContainer' ,            
13971             html :  this.html, // fix changable?
13972             cls : 'w-100 ',
13973             listeners : {
13974                 'click' : function(btn, e) {
13975                     t.onClick(e);
13976                 }
13977             }
13978         });
13979         
13980         
13981         
13982         
13983         this.urlAPI = (window.createObjectURL && window) || 
13984                                 (window.URL && URL.revokeObjectURL && URL) || 
13985                                 (window.webkitURL && webkitURL);
13986                         
13987          
13988          
13989          
13990         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13991         
13992         this.selectorEl.on('change', this.onFileSelected, this);
13993         if (this.images) {
13994             var t = this;
13995             this.images.forEach(function(img) {
13996                 t.addCard(img)
13997             });
13998             this.images = false;
13999         }
14000         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14001          
14002        
14003     },
14004     
14005    
14006     onClick : function(e)
14007     {
14008         e.preventDefault();
14009          
14010         this.selectorEl.dom.click();
14011          
14012     },
14013     
14014     onFileSelected : function(e)
14015     {
14016         e.preventDefault();
14017         
14018         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14019             return;
14020         }
14021         
14022         Roo.each(this.selectorEl.dom.files, function(file){    
14023             this.addFile(file);
14024         }, this);
14025          
14026     },
14027     
14028       
14029     
14030       
14031     
14032     addFile : function(file)
14033     {
14034            
14035         if(typeof(file) === 'string'){
14036             throw "Add file by name?"; // should not happen
14037             return;
14038         }
14039         
14040         if(!file || !this.urlAPI){
14041             return;
14042         }
14043         
14044         // file;
14045         // file.type;
14046         
14047         var _this = this;
14048         
14049         
14050         var url = _this.urlAPI.createObjectURL( file);
14051            
14052         this.addCard({
14053             id : Roo.bootstrap.CardUploader.ID--,
14054             is_uploaded : false,
14055             src : url,
14056             srcfile : file,
14057             title : file.name,
14058             mimetype : file.type,
14059             preview : false,
14060             is_deleted : 0
14061         });
14062         
14063     },
14064     
14065     /**
14066      * addCard - add an Attachment to the uploader
14067      * @param data - the data about the image to upload
14068      *
14069      * {
14070           id : 123
14071           title : "Title of file",
14072           is_uploaded : false,
14073           src : "http://.....",
14074           srcfile : { the File upload object },
14075           mimetype : file.type,
14076           preview : false,
14077           is_deleted : 0
14078           .. any other data...
14079         }
14080      *
14081      * 
14082     */
14083     
14084     addCard : function (data)
14085     {
14086         // hidden input element?
14087         // if the file is not an image...
14088         //then we need to use something other that and header_image
14089         var t = this;
14090         //   remove.....
14091         var footer = [
14092             {
14093                 xns : Roo.bootstrap,
14094                 xtype : 'CardFooter',
14095                  items: [
14096                     {
14097                         xns : Roo.bootstrap,
14098                         xtype : 'Element',
14099                         cls : 'd-flex',
14100                         items : [
14101                             
14102                             {
14103                                 xns : Roo.bootstrap,
14104                                 xtype : 'Button',
14105                                 html : String.format("<small>{0}</small>", data.title),
14106                                 cls : 'col-10 text-left',
14107                                 size: 'sm',
14108                                 weight: 'link',
14109                                 fa : 'download',
14110                                 listeners : {
14111                                     click : function() {
14112                                      
14113                                         t.fireEvent( "download", t, data );
14114                                     }
14115                                 }
14116                             },
14117                           
14118                             {
14119                                 xns : Roo.bootstrap,
14120                                 xtype : 'Button',
14121                                 style: 'max-height: 28px; ',
14122                                 size : 'sm',
14123                                 weight: 'danger',
14124                                 cls : 'col-2',
14125                                 fa : 'times',
14126                                 listeners : {
14127                                     click : function() {
14128                                         t.removeCard(data.id)
14129                                     }
14130                                 }
14131                             }
14132                         ]
14133                     }
14134                     
14135                 ] 
14136             }
14137             
14138         ];
14139         
14140         var cn = this.addxtype(
14141             {
14142                  
14143                 xns : Roo.bootstrap,
14144                 xtype : 'Card',
14145                 closeable : true,
14146                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14147                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14148                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14149                 data : data,
14150                 html : false,
14151                  
14152                 items : footer,
14153                 initEvents : function() {
14154                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14155                     var card = this;
14156                     this.imgEl = this.el.select('.card-img-top').first();
14157                     if (this.imgEl) {
14158                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14159                         this.imgEl.set({ 'pointer' : 'cursor' });
14160                                   
14161                     }
14162                     this.getCardFooter().addClass('p-1');
14163                     
14164                   
14165                 }
14166                 
14167             }
14168         );
14169         // dont' really need ot update items.
14170         // this.items.push(cn);
14171         this.fileCollection.add(cn);
14172         
14173         if (!data.srcfile) {
14174             this.updateInput();
14175             return;
14176         }
14177             
14178         var _t = this;
14179         var reader = new FileReader();
14180         reader.addEventListener("load", function() {  
14181             data.srcdata =  reader.result;
14182             _t.updateInput();
14183         });
14184         reader.readAsDataURL(data.srcfile);
14185         
14186         
14187         
14188     },
14189     removeCard : function(id)
14190     {
14191         
14192         var card  = this.fileCollection.get(id);
14193         card.data.is_deleted = 1;
14194         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14195         //this.fileCollection.remove(card);
14196         //this.items = this.items.filter(function(e) { return e != card });
14197         // dont' really need ot update items.
14198         card.el.dom.parentNode.removeChild(card.el.dom);
14199         this.updateInput();
14200
14201         
14202     },
14203     reset: function()
14204     {
14205         this.fileCollection.each(function(card) {
14206             if (card.el.dom && card.el.dom.parentNode) {
14207                 card.el.dom.parentNode.removeChild(card.el.dom);
14208             }
14209         });
14210         this.fileCollection.clear();
14211         this.updateInput();
14212     },
14213     
14214     updateInput : function()
14215     {
14216          var data = [];
14217         this.fileCollection.each(function(e) {
14218             data.push(e.data);
14219             
14220         });
14221         this.inputEl().dom.value = JSON.stringify(data);
14222         
14223         
14224         
14225     }
14226     
14227     
14228 });
14229
14230
14231 Roo.bootstrap.CardUploader.ID = -1;/*
14232  * Based on:
14233  * Ext JS Library 1.1.1
14234  * Copyright(c) 2006-2007, Ext JS, LLC.
14235  *
14236  * Originally Released Under LGPL - original licence link has changed is not relivant.
14237  *
14238  * Fork - LGPL
14239  * <script type="text/javascript">
14240  */
14241
14242
14243 /**
14244  * @class Roo.data.SortTypes
14245  * @singleton
14246  * Defines the default sorting (casting?) comparison functions used when sorting data.
14247  */
14248 Roo.data.SortTypes = {
14249     /**
14250      * Default sort that does nothing
14251      * @param {Mixed} s The value being converted
14252      * @return {Mixed} The comparison value
14253      */
14254     none : function(s){
14255         return s;
14256     },
14257     
14258     /**
14259      * The regular expression used to strip tags
14260      * @type {RegExp}
14261      * @property
14262      */
14263     stripTagsRE : /<\/?[^>]+>/gi,
14264     
14265     /**
14266      * Strips all HTML tags to sort on text only
14267      * @param {Mixed} s The value being converted
14268      * @return {String} The comparison value
14269      */
14270     asText : function(s){
14271         return String(s).replace(this.stripTagsRE, "");
14272     },
14273     
14274     /**
14275      * Strips all HTML tags to sort on text only - Case insensitive
14276      * @param {Mixed} s The value being converted
14277      * @return {String} The comparison value
14278      */
14279     asUCText : function(s){
14280         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14281     },
14282     
14283     /**
14284      * Case insensitive string
14285      * @param {Mixed} s The value being converted
14286      * @return {String} The comparison value
14287      */
14288     asUCString : function(s) {
14289         return String(s).toUpperCase();
14290     },
14291     
14292     /**
14293      * Date sorting
14294      * @param {Mixed} s The value being converted
14295      * @return {Number} The comparison value
14296      */
14297     asDate : function(s) {
14298         if(!s){
14299             return 0;
14300         }
14301         if(s instanceof Date){
14302             return s.getTime();
14303         }
14304         return Date.parse(String(s));
14305     },
14306     
14307     /**
14308      * Float sorting
14309      * @param {Mixed} s The value being converted
14310      * @return {Float} The comparison value
14311      */
14312     asFloat : function(s) {
14313         var val = parseFloat(String(s).replace(/,/g, ""));
14314         if(isNaN(val)) {
14315             val = 0;
14316         }
14317         return val;
14318     },
14319     
14320     /**
14321      * Integer sorting
14322      * @param {Mixed} s The value being converted
14323      * @return {Number} The comparison value
14324      */
14325     asInt : function(s) {
14326         var val = parseInt(String(s).replace(/,/g, ""));
14327         if(isNaN(val)) {
14328             val = 0;
14329         }
14330         return val;
14331     }
14332 };/*
14333  * Based on:
14334  * Ext JS Library 1.1.1
14335  * Copyright(c) 2006-2007, Ext JS, LLC.
14336  *
14337  * Originally Released Under LGPL - original licence link has changed is not relivant.
14338  *
14339  * Fork - LGPL
14340  * <script type="text/javascript">
14341  */
14342
14343 /**
14344 * @class Roo.data.Record
14345  * Instances of this class encapsulate both record <em>definition</em> information, and record
14346  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14347  * to access Records cached in an {@link Roo.data.Store} object.<br>
14348  * <p>
14349  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14350  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14351  * objects.<br>
14352  * <p>
14353  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14354  * @constructor
14355  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14356  * {@link #create}. The parameters are the same.
14357  * @param {Array} data An associative Array of data values keyed by the field name.
14358  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14359  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14360  * not specified an integer id is generated.
14361  */
14362 Roo.data.Record = function(data, id){
14363     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14364     this.data = data;
14365 };
14366
14367 /**
14368  * Generate a constructor for a specific record layout.
14369  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14370  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14371  * Each field definition object may contain the following properties: <ul>
14372  * <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,
14373  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14374  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14375  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14376  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14377  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14378  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14379  * this may be omitted.</p></li>
14380  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14381  * <ul><li>auto (Default, implies no conversion)</li>
14382  * <li>string</li>
14383  * <li>int</li>
14384  * <li>float</li>
14385  * <li>boolean</li>
14386  * <li>date</li></ul></p></li>
14387  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14388  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14389  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14390  * by the Reader into an object that will be stored in the Record. It is passed the
14391  * following parameters:<ul>
14392  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14393  * </ul></p></li>
14394  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14395  * </ul>
14396  * <br>usage:<br><pre><code>
14397 var TopicRecord = Roo.data.Record.create(
14398     {name: 'title', mapping: 'topic_title'},
14399     {name: 'author', mapping: 'username'},
14400     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14401     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14402     {name: 'lastPoster', mapping: 'user2'},
14403     {name: 'excerpt', mapping: 'post_text'}
14404 );
14405
14406 var myNewRecord = new TopicRecord({
14407     title: 'Do my job please',
14408     author: 'noobie',
14409     totalPosts: 1,
14410     lastPost: new Date(),
14411     lastPoster: 'Animal',
14412     excerpt: 'No way dude!'
14413 });
14414 myStore.add(myNewRecord);
14415 </code></pre>
14416  * @method create
14417  * @static
14418  */
14419 Roo.data.Record.create = function(o){
14420     var f = function(){
14421         f.superclass.constructor.apply(this, arguments);
14422     };
14423     Roo.extend(f, Roo.data.Record);
14424     var p = f.prototype;
14425     p.fields = new Roo.util.MixedCollection(false, function(field){
14426         return field.name;
14427     });
14428     for(var i = 0, len = o.length; i < len; i++){
14429         p.fields.add(new Roo.data.Field(o[i]));
14430     }
14431     f.getField = function(name){
14432         return p.fields.get(name);  
14433     };
14434     return f;
14435 };
14436
14437 Roo.data.Record.AUTO_ID = 1000;
14438 Roo.data.Record.EDIT = 'edit';
14439 Roo.data.Record.REJECT = 'reject';
14440 Roo.data.Record.COMMIT = 'commit';
14441
14442 Roo.data.Record.prototype = {
14443     /**
14444      * Readonly flag - true if this record has been modified.
14445      * @type Boolean
14446      */
14447     dirty : false,
14448     editing : false,
14449     error: null,
14450     modified: null,
14451
14452     // private
14453     join : function(store){
14454         this.store = store;
14455     },
14456
14457     /**
14458      * Set the named field to the specified value.
14459      * @param {String} name The name of the field to set.
14460      * @param {Object} value The value to set the field to.
14461      */
14462     set : function(name, value){
14463         if(this.data[name] == value){
14464             return;
14465         }
14466         this.dirty = true;
14467         if(!this.modified){
14468             this.modified = {};
14469         }
14470         if(typeof this.modified[name] == 'undefined'){
14471             this.modified[name] = this.data[name];
14472         }
14473         this.data[name] = value;
14474         if(!this.editing && this.store){
14475             this.store.afterEdit(this);
14476         }       
14477     },
14478
14479     /**
14480      * Get the value of the named field.
14481      * @param {String} name The name of the field to get the value of.
14482      * @return {Object} The value of the field.
14483      */
14484     get : function(name){
14485         return this.data[name]; 
14486     },
14487
14488     // private
14489     beginEdit : function(){
14490         this.editing = true;
14491         this.modified = {}; 
14492     },
14493
14494     // private
14495     cancelEdit : function(){
14496         this.editing = false;
14497         delete this.modified;
14498     },
14499
14500     // private
14501     endEdit : function(){
14502         this.editing = false;
14503         if(this.dirty && this.store){
14504             this.store.afterEdit(this);
14505         }
14506     },
14507
14508     /**
14509      * Usually called by the {@link Roo.data.Store} which owns the Record.
14510      * Rejects all changes made to the Record since either creation, or the last commit operation.
14511      * Modified fields are reverted to their original values.
14512      * <p>
14513      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14514      * of reject operations.
14515      */
14516     reject : function(){
14517         var m = this.modified;
14518         for(var n in m){
14519             if(typeof m[n] != "function"){
14520                 this.data[n] = m[n];
14521             }
14522         }
14523         this.dirty = false;
14524         delete this.modified;
14525         this.editing = false;
14526         if(this.store){
14527             this.store.afterReject(this);
14528         }
14529     },
14530
14531     /**
14532      * Usually called by the {@link Roo.data.Store} which owns the Record.
14533      * Commits all changes made to the Record since either creation, or the last commit operation.
14534      * <p>
14535      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14536      * of commit operations.
14537      */
14538     commit : function(){
14539         this.dirty = false;
14540         delete this.modified;
14541         this.editing = false;
14542         if(this.store){
14543             this.store.afterCommit(this);
14544         }
14545     },
14546
14547     // private
14548     hasError : function(){
14549         return this.error != null;
14550     },
14551
14552     // private
14553     clearError : function(){
14554         this.error = null;
14555     },
14556
14557     /**
14558      * Creates a copy of this record.
14559      * @param {String} id (optional) A new record id if you don't want to use this record's id
14560      * @return {Record}
14561      */
14562     copy : function(newId) {
14563         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14564     }
14565 };/*
14566  * Based on:
14567  * Ext JS Library 1.1.1
14568  * Copyright(c) 2006-2007, Ext JS, LLC.
14569  *
14570  * Originally Released Under LGPL - original licence link has changed is not relivant.
14571  *
14572  * Fork - LGPL
14573  * <script type="text/javascript">
14574  */
14575
14576
14577
14578 /**
14579  * @class Roo.data.Store
14580  * @extends Roo.util.Observable
14581  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14582  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14583  * <p>
14584  * 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
14585  * has no knowledge of the format of the data returned by the Proxy.<br>
14586  * <p>
14587  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14588  * instances from the data object. These records are cached and made available through accessor functions.
14589  * @constructor
14590  * Creates a new Store.
14591  * @param {Object} config A config object containing the objects needed for the Store to access data,
14592  * and read the data into Records.
14593  */
14594 Roo.data.Store = function(config){
14595     this.data = new Roo.util.MixedCollection(false);
14596     this.data.getKey = function(o){
14597         return o.id;
14598     };
14599     this.baseParams = {};
14600     // private
14601     this.paramNames = {
14602         "start" : "start",
14603         "limit" : "limit",
14604         "sort" : "sort",
14605         "dir" : "dir",
14606         "multisort" : "_multisort"
14607     };
14608
14609     if(config && config.data){
14610         this.inlineData = config.data;
14611         delete config.data;
14612     }
14613
14614     Roo.apply(this, config);
14615     
14616     if(this.reader){ // reader passed
14617         this.reader = Roo.factory(this.reader, Roo.data);
14618         this.reader.xmodule = this.xmodule || false;
14619         if(!this.recordType){
14620             this.recordType = this.reader.recordType;
14621         }
14622         if(this.reader.onMetaChange){
14623             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14624         }
14625     }
14626
14627     if(this.recordType){
14628         this.fields = this.recordType.prototype.fields;
14629     }
14630     this.modified = [];
14631
14632     this.addEvents({
14633         /**
14634          * @event datachanged
14635          * Fires when the data cache has changed, and a widget which is using this Store
14636          * as a Record cache should refresh its view.
14637          * @param {Store} this
14638          */
14639         datachanged : true,
14640         /**
14641          * @event metachange
14642          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14643          * @param {Store} this
14644          * @param {Object} meta The JSON metadata
14645          */
14646         metachange : true,
14647         /**
14648          * @event add
14649          * Fires when Records have been added to the Store
14650          * @param {Store} this
14651          * @param {Roo.data.Record[]} records The array of Records added
14652          * @param {Number} index The index at which the record(s) were added
14653          */
14654         add : true,
14655         /**
14656          * @event remove
14657          * Fires when a Record has been removed from the Store
14658          * @param {Store} this
14659          * @param {Roo.data.Record} record The Record that was removed
14660          * @param {Number} index The index at which the record was removed
14661          */
14662         remove : true,
14663         /**
14664          * @event update
14665          * Fires when a Record has been updated
14666          * @param {Store} this
14667          * @param {Roo.data.Record} record The Record that was updated
14668          * @param {String} operation The update operation being performed.  Value may be one of:
14669          * <pre><code>
14670  Roo.data.Record.EDIT
14671  Roo.data.Record.REJECT
14672  Roo.data.Record.COMMIT
14673          * </code></pre>
14674          */
14675         update : true,
14676         /**
14677          * @event clear
14678          * Fires when the data cache has been cleared.
14679          * @param {Store} this
14680          */
14681         clear : true,
14682         /**
14683          * @event beforeload
14684          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14685          * the load action will be canceled.
14686          * @param {Store} this
14687          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14688          */
14689         beforeload : true,
14690         /**
14691          * @event beforeloadadd
14692          * Fires after a new set of Records has been loaded.
14693          * @param {Store} this
14694          * @param {Roo.data.Record[]} records The Records that were loaded
14695          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14696          */
14697         beforeloadadd : true,
14698         /**
14699          * @event load
14700          * Fires after a new set of Records has been loaded, before they are added to the store.
14701          * @param {Store} this
14702          * @param {Roo.data.Record[]} records The Records that were loaded
14703          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14704          * @params {Object} return from reader
14705          */
14706         load : true,
14707         /**
14708          * @event loadexception
14709          * Fires if an exception occurs in the Proxy during loading.
14710          * Called with the signature of the Proxy's "loadexception" event.
14711          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14712          * 
14713          * @param {Proxy} 
14714          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14715          * @param {Object} load options 
14716          * @param {Object} jsonData from your request (normally this contains the Exception)
14717          */
14718         loadexception : true
14719     });
14720     
14721     if(this.proxy){
14722         this.proxy = Roo.factory(this.proxy, Roo.data);
14723         this.proxy.xmodule = this.xmodule || false;
14724         this.relayEvents(this.proxy,  ["loadexception"]);
14725     }
14726     this.sortToggle = {};
14727     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14728
14729     Roo.data.Store.superclass.constructor.call(this);
14730
14731     if(this.inlineData){
14732         this.loadData(this.inlineData);
14733         delete this.inlineData;
14734     }
14735 };
14736
14737 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14738      /**
14739     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14740     * without a remote query - used by combo/forms at present.
14741     */
14742     
14743     /**
14744     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14745     */
14746     /**
14747     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14748     */
14749     /**
14750     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14751     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14752     */
14753     /**
14754     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14755     * on any HTTP request
14756     */
14757     /**
14758     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14759     */
14760     /**
14761     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14762     */
14763     multiSort: false,
14764     /**
14765     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14766     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14767     */
14768     remoteSort : false,
14769
14770     /**
14771     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14772      * loaded or when a record is removed. (defaults to false).
14773     */
14774     pruneModifiedRecords : false,
14775
14776     // private
14777     lastOptions : null,
14778
14779     /**
14780      * Add Records to the Store and fires the add event.
14781      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14782      */
14783     add : function(records){
14784         records = [].concat(records);
14785         for(var i = 0, len = records.length; i < len; i++){
14786             records[i].join(this);
14787         }
14788         var index = this.data.length;
14789         this.data.addAll(records);
14790         this.fireEvent("add", this, records, index);
14791     },
14792
14793     /**
14794      * Remove a Record from the Store and fires the remove event.
14795      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14796      */
14797     remove : function(record){
14798         var index = this.data.indexOf(record);
14799         this.data.removeAt(index);
14800  
14801         if(this.pruneModifiedRecords){
14802             this.modified.remove(record);
14803         }
14804         this.fireEvent("remove", this, record, index);
14805     },
14806
14807     /**
14808      * Remove all Records from the Store and fires the clear event.
14809      */
14810     removeAll : function(){
14811         this.data.clear();
14812         if(this.pruneModifiedRecords){
14813             this.modified = [];
14814         }
14815         this.fireEvent("clear", this);
14816     },
14817
14818     /**
14819      * Inserts Records to the Store at the given index and fires the add event.
14820      * @param {Number} index The start index at which to insert the passed Records.
14821      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14822      */
14823     insert : function(index, records){
14824         records = [].concat(records);
14825         for(var i = 0, len = records.length; i < len; i++){
14826             this.data.insert(index, records[i]);
14827             records[i].join(this);
14828         }
14829         this.fireEvent("add", this, records, index);
14830     },
14831
14832     /**
14833      * Get the index within the cache of the passed Record.
14834      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14835      * @return {Number} The index of the passed Record. Returns -1 if not found.
14836      */
14837     indexOf : function(record){
14838         return this.data.indexOf(record);
14839     },
14840
14841     /**
14842      * Get the index within the cache of the Record with the passed id.
14843      * @param {String} id The id of the Record to find.
14844      * @return {Number} The index of the Record. Returns -1 if not found.
14845      */
14846     indexOfId : function(id){
14847         return this.data.indexOfKey(id);
14848     },
14849
14850     /**
14851      * Get the Record with the specified id.
14852      * @param {String} id The id of the Record to find.
14853      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14854      */
14855     getById : function(id){
14856         return this.data.key(id);
14857     },
14858
14859     /**
14860      * Get the Record at the specified index.
14861      * @param {Number} index The index of the Record to find.
14862      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14863      */
14864     getAt : function(index){
14865         return this.data.itemAt(index);
14866     },
14867
14868     /**
14869      * Returns a range of Records between specified indices.
14870      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14871      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14872      * @return {Roo.data.Record[]} An array of Records
14873      */
14874     getRange : function(start, end){
14875         return this.data.getRange(start, end);
14876     },
14877
14878     // private
14879     storeOptions : function(o){
14880         o = Roo.apply({}, o);
14881         delete o.callback;
14882         delete o.scope;
14883         this.lastOptions = o;
14884     },
14885
14886     /**
14887      * Loads the Record cache from the configured Proxy using the configured Reader.
14888      * <p>
14889      * If using remote paging, then the first load call must specify the <em>start</em>
14890      * and <em>limit</em> properties in the options.params property to establish the initial
14891      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14892      * <p>
14893      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14894      * and this call will return before the new data has been loaded. Perform any post-processing
14895      * in a callback function, or in a "load" event handler.</strong>
14896      * <p>
14897      * @param {Object} options An object containing properties which control loading options:<ul>
14898      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14899      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14900      * passed the following arguments:<ul>
14901      * <li>r : Roo.data.Record[]</li>
14902      * <li>options: Options object from the load call</li>
14903      * <li>success: Boolean success indicator</li></ul></li>
14904      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14905      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14906      * </ul>
14907      */
14908     load : function(options){
14909         options = options || {};
14910         if(this.fireEvent("beforeload", this, options) !== false){
14911             this.storeOptions(options);
14912             var p = Roo.apply(options.params || {}, this.baseParams);
14913             // if meta was not loaded from remote source.. try requesting it.
14914             if (!this.reader.metaFromRemote) {
14915                 p._requestMeta = 1;
14916             }
14917             if(this.sortInfo && this.remoteSort){
14918                 var pn = this.paramNames;
14919                 p[pn["sort"]] = this.sortInfo.field;
14920                 p[pn["dir"]] = this.sortInfo.direction;
14921             }
14922             if (this.multiSort) {
14923                 var pn = this.paramNames;
14924                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14925             }
14926             
14927             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14928         }
14929     },
14930
14931     /**
14932      * Reloads the Record cache from the configured Proxy using the configured Reader and
14933      * the options from the last load operation performed.
14934      * @param {Object} options (optional) An object containing properties which may override the options
14935      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14936      * the most recently used options are reused).
14937      */
14938     reload : function(options){
14939         this.load(Roo.applyIf(options||{}, this.lastOptions));
14940     },
14941
14942     // private
14943     // Called as a callback by the Reader during a load operation.
14944     loadRecords : function(o, options, success){
14945         if(!o || success === false){
14946             if(success !== false){
14947                 this.fireEvent("load", this, [], options, o);
14948             }
14949             if(options.callback){
14950                 options.callback.call(options.scope || this, [], options, false);
14951             }
14952             return;
14953         }
14954         // if data returned failure - throw an exception.
14955         if (o.success === false) {
14956             // show a message if no listener is registered.
14957             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14958                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14959             }
14960             // loadmask wil be hooked into this..
14961             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14962             return;
14963         }
14964         var r = o.records, t = o.totalRecords || r.length;
14965         
14966         this.fireEvent("beforeloadadd", this, r, options, o);
14967         
14968         if(!options || options.add !== true){
14969             if(this.pruneModifiedRecords){
14970                 this.modified = [];
14971             }
14972             for(var i = 0, len = r.length; i < len; i++){
14973                 r[i].join(this);
14974             }
14975             if(this.snapshot){
14976                 this.data = this.snapshot;
14977                 delete this.snapshot;
14978             }
14979             this.data.clear();
14980             this.data.addAll(r);
14981             this.totalLength = t;
14982             this.applySort();
14983             this.fireEvent("datachanged", this);
14984         }else{
14985             this.totalLength = Math.max(t, this.data.length+r.length);
14986             this.add(r);
14987         }
14988         
14989         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14990                 
14991             var e = new Roo.data.Record({});
14992
14993             e.set(this.parent.displayField, this.parent.emptyTitle);
14994             e.set(this.parent.valueField, '');
14995
14996             this.insert(0, e);
14997         }
14998             
14999         this.fireEvent("load", this, r, options, o);
15000         if(options.callback){
15001             options.callback.call(options.scope || this, r, options, true);
15002         }
15003     },
15004
15005
15006     /**
15007      * Loads data from a passed data block. A Reader which understands the format of the data
15008      * must have been configured in the constructor.
15009      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15010      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15011      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15012      */
15013     loadData : function(o, append){
15014         var r = this.reader.readRecords(o);
15015         this.loadRecords(r, {add: append}, true);
15016     },
15017     
15018      /**
15019      * using 'cn' the nested child reader read the child array into it's child stores.
15020      * @param {Object} rec The record with a 'children array
15021      */
15022     loadDataFromChildren : function(rec)
15023     {
15024         this.loadData(this.reader.toLoadData(rec));
15025     },
15026     
15027
15028     /**
15029      * Gets the number of cached records.
15030      * <p>
15031      * <em>If using paging, this may not be the total size of the dataset. If the data object
15032      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15033      * the data set size</em>
15034      */
15035     getCount : function(){
15036         return this.data.length || 0;
15037     },
15038
15039     /**
15040      * Gets the total number of records in the dataset as returned by the server.
15041      * <p>
15042      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15043      * the dataset size</em>
15044      */
15045     getTotalCount : function(){
15046         return this.totalLength || 0;
15047     },
15048
15049     /**
15050      * Returns the sort state of the Store as an object with two properties:
15051      * <pre><code>
15052  field {String} The name of the field by which the Records are sorted
15053  direction {String} The sort order, "ASC" or "DESC"
15054      * </code></pre>
15055      */
15056     getSortState : function(){
15057         return this.sortInfo;
15058     },
15059
15060     // private
15061     applySort : function(){
15062         if(this.sortInfo && !this.remoteSort){
15063             var s = this.sortInfo, f = s.field;
15064             var st = this.fields.get(f).sortType;
15065             var fn = function(r1, r2){
15066                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15067                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15068             };
15069             this.data.sort(s.direction, fn);
15070             if(this.snapshot && this.snapshot != this.data){
15071                 this.snapshot.sort(s.direction, fn);
15072             }
15073         }
15074     },
15075
15076     /**
15077      * Sets the default sort column and order to be used by the next load operation.
15078      * @param {String} fieldName The name of the field to sort by.
15079      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15080      */
15081     setDefaultSort : function(field, dir){
15082         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15083     },
15084
15085     /**
15086      * Sort the Records.
15087      * If remote sorting is used, the sort is performed on the server, and the cache is
15088      * reloaded. If local sorting is used, the cache is sorted internally.
15089      * @param {String} fieldName The name of the field to sort by.
15090      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15091      */
15092     sort : function(fieldName, dir){
15093         var f = this.fields.get(fieldName);
15094         if(!dir){
15095             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15096             
15097             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15098                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15099             }else{
15100                 dir = f.sortDir;
15101             }
15102         }
15103         this.sortToggle[f.name] = dir;
15104         this.sortInfo = {field: f.name, direction: dir};
15105         if(!this.remoteSort){
15106             this.applySort();
15107             this.fireEvent("datachanged", this);
15108         }else{
15109             this.load(this.lastOptions);
15110         }
15111     },
15112
15113     /**
15114      * Calls the specified function for each of the Records in the cache.
15115      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15116      * Returning <em>false</em> aborts and exits the iteration.
15117      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15118      */
15119     each : function(fn, scope){
15120         this.data.each(fn, scope);
15121     },
15122
15123     /**
15124      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15125      * (e.g., during paging).
15126      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15127      */
15128     getModifiedRecords : function(){
15129         return this.modified;
15130     },
15131
15132     // private
15133     createFilterFn : function(property, value, anyMatch){
15134         if(!value.exec){ // not a regex
15135             value = String(value);
15136             if(value.length == 0){
15137                 return false;
15138             }
15139             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15140         }
15141         return function(r){
15142             return value.test(r.data[property]);
15143         };
15144     },
15145
15146     /**
15147      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15148      * @param {String} property A field on your records
15149      * @param {Number} start The record index to start at (defaults to 0)
15150      * @param {Number} end The last record index to include (defaults to length - 1)
15151      * @return {Number} The sum
15152      */
15153     sum : function(property, start, end){
15154         var rs = this.data.items, v = 0;
15155         start = start || 0;
15156         end = (end || end === 0) ? end : rs.length-1;
15157
15158         for(var i = start; i <= end; i++){
15159             v += (rs[i].data[property] || 0);
15160         }
15161         return v;
15162     },
15163
15164     /**
15165      * Filter the records by a specified property.
15166      * @param {String} field A field on your records
15167      * @param {String/RegExp} value Either a string that the field
15168      * should start with or a RegExp to test against the field
15169      * @param {Boolean} anyMatch True to match any part not just the beginning
15170      */
15171     filter : function(property, value, anyMatch){
15172         var fn = this.createFilterFn(property, value, anyMatch);
15173         return fn ? this.filterBy(fn) : this.clearFilter();
15174     },
15175
15176     /**
15177      * Filter by a function. The specified function will be called with each
15178      * record in this data source. If the function returns true the record is included,
15179      * otherwise it is filtered.
15180      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15181      * @param {Object} scope (optional) The scope of the function (defaults to this)
15182      */
15183     filterBy : function(fn, scope){
15184         this.snapshot = this.snapshot || this.data;
15185         this.data = this.queryBy(fn, scope||this);
15186         this.fireEvent("datachanged", this);
15187     },
15188
15189     /**
15190      * Query the records by a specified property.
15191      * @param {String} field A field on your records
15192      * @param {String/RegExp} value Either a string that the field
15193      * should start with or a RegExp to test against the field
15194      * @param {Boolean} anyMatch True to match any part not just the beginning
15195      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15196      */
15197     query : function(property, value, anyMatch){
15198         var fn = this.createFilterFn(property, value, anyMatch);
15199         return fn ? this.queryBy(fn) : this.data.clone();
15200     },
15201
15202     /**
15203      * Query by a function. The specified function will be called with each
15204      * record in this data source. If the function returns true the record is included
15205      * in the results.
15206      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15207      * @param {Object} scope (optional) The scope of the function (defaults to this)
15208       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15209      **/
15210     queryBy : function(fn, scope){
15211         var data = this.snapshot || this.data;
15212         return data.filterBy(fn, scope||this);
15213     },
15214
15215     /**
15216      * Collects unique values for a particular dataIndex from this store.
15217      * @param {String} dataIndex The property to collect
15218      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15219      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15220      * @return {Array} An array of the unique values
15221      **/
15222     collect : function(dataIndex, allowNull, bypassFilter){
15223         var d = (bypassFilter === true && this.snapshot) ?
15224                 this.snapshot.items : this.data.items;
15225         var v, sv, r = [], l = {};
15226         for(var i = 0, len = d.length; i < len; i++){
15227             v = d[i].data[dataIndex];
15228             sv = String(v);
15229             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15230                 l[sv] = true;
15231                 r[r.length] = v;
15232             }
15233         }
15234         return r;
15235     },
15236
15237     /**
15238      * Revert to a view of the Record cache with no filtering applied.
15239      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15240      */
15241     clearFilter : function(suppressEvent){
15242         if(this.snapshot && this.snapshot != this.data){
15243             this.data = this.snapshot;
15244             delete this.snapshot;
15245             if(suppressEvent !== true){
15246                 this.fireEvent("datachanged", this);
15247             }
15248         }
15249     },
15250
15251     // private
15252     afterEdit : function(record){
15253         if(this.modified.indexOf(record) == -1){
15254             this.modified.push(record);
15255         }
15256         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15257     },
15258     
15259     // private
15260     afterReject : function(record){
15261         this.modified.remove(record);
15262         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15263     },
15264
15265     // private
15266     afterCommit : function(record){
15267         this.modified.remove(record);
15268         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15269     },
15270
15271     /**
15272      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15273      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15274      */
15275     commitChanges : function(){
15276         var m = this.modified.slice(0);
15277         this.modified = [];
15278         for(var i = 0, len = m.length; i < len; i++){
15279             m[i].commit();
15280         }
15281     },
15282
15283     /**
15284      * Cancel outstanding changes on all changed records.
15285      */
15286     rejectChanges : function(){
15287         var m = this.modified.slice(0);
15288         this.modified = [];
15289         for(var i = 0, len = m.length; i < len; i++){
15290             m[i].reject();
15291         }
15292     },
15293
15294     onMetaChange : function(meta, rtype, o){
15295         this.recordType = rtype;
15296         this.fields = rtype.prototype.fields;
15297         delete this.snapshot;
15298         this.sortInfo = meta.sortInfo || this.sortInfo;
15299         this.modified = [];
15300         this.fireEvent('metachange', this, this.reader.meta);
15301     },
15302     
15303     moveIndex : function(data, type)
15304     {
15305         var index = this.indexOf(data);
15306         
15307         var newIndex = index + type;
15308         
15309         this.remove(data);
15310         
15311         this.insert(newIndex, data);
15312         
15313     }
15314 });/*
15315  * Based on:
15316  * Ext JS Library 1.1.1
15317  * Copyright(c) 2006-2007, Ext JS, LLC.
15318  *
15319  * Originally Released Under LGPL - original licence link has changed is not relivant.
15320  *
15321  * Fork - LGPL
15322  * <script type="text/javascript">
15323  */
15324
15325 /**
15326  * @class Roo.data.SimpleStore
15327  * @extends Roo.data.Store
15328  * Small helper class to make creating Stores from Array data easier.
15329  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15330  * @cfg {Array} fields An array of field definition objects, or field name strings.
15331  * @cfg {Object} an existing reader (eg. copied from another store)
15332  * @cfg {Array} data The multi-dimensional array of data
15333  * @constructor
15334  * @param {Object} config
15335  */
15336 Roo.data.SimpleStore = function(config)
15337 {
15338     Roo.data.SimpleStore.superclass.constructor.call(this, {
15339         isLocal : true,
15340         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15341                 id: config.id
15342             },
15343             Roo.data.Record.create(config.fields)
15344         ),
15345         proxy : new Roo.data.MemoryProxy(config.data)
15346     });
15347     this.load();
15348 };
15349 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15350  * Based on:
15351  * Ext JS Library 1.1.1
15352  * Copyright(c) 2006-2007, Ext JS, LLC.
15353  *
15354  * Originally Released Under LGPL - original licence link has changed is not relivant.
15355  *
15356  * Fork - LGPL
15357  * <script type="text/javascript">
15358  */
15359
15360 /**
15361 /**
15362  * @extends Roo.data.Store
15363  * @class Roo.data.JsonStore
15364  * Small helper class to make creating Stores for JSON data easier. <br/>
15365 <pre><code>
15366 var store = new Roo.data.JsonStore({
15367     url: 'get-images.php',
15368     root: 'images',
15369     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15370 });
15371 </code></pre>
15372  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15373  * JsonReader and HttpProxy (unless inline data is provided).</b>
15374  * @cfg {Array} fields An array of field definition objects, or field name strings.
15375  * @constructor
15376  * @param {Object} config
15377  */
15378 Roo.data.JsonStore = function(c){
15379     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15380         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15381         reader: new Roo.data.JsonReader(c, c.fields)
15382     }));
15383 };
15384 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15385  * Based on:
15386  * Ext JS Library 1.1.1
15387  * Copyright(c) 2006-2007, Ext JS, LLC.
15388  *
15389  * Originally Released Under LGPL - original licence link has changed is not relivant.
15390  *
15391  * Fork - LGPL
15392  * <script type="text/javascript">
15393  */
15394
15395  
15396 Roo.data.Field = function(config){
15397     if(typeof config == "string"){
15398         config = {name: config};
15399     }
15400     Roo.apply(this, config);
15401     
15402     if(!this.type){
15403         this.type = "auto";
15404     }
15405     
15406     var st = Roo.data.SortTypes;
15407     // named sortTypes are supported, here we look them up
15408     if(typeof this.sortType == "string"){
15409         this.sortType = st[this.sortType];
15410     }
15411     
15412     // set default sortType for strings and dates
15413     if(!this.sortType){
15414         switch(this.type){
15415             case "string":
15416                 this.sortType = st.asUCString;
15417                 break;
15418             case "date":
15419                 this.sortType = st.asDate;
15420                 break;
15421             default:
15422                 this.sortType = st.none;
15423         }
15424     }
15425
15426     // define once
15427     var stripRe = /[\$,%]/g;
15428
15429     // prebuilt conversion function for this field, instead of
15430     // switching every time we're reading a value
15431     if(!this.convert){
15432         var cv, dateFormat = this.dateFormat;
15433         switch(this.type){
15434             case "":
15435             case "auto":
15436             case undefined:
15437                 cv = function(v){ return v; };
15438                 break;
15439             case "string":
15440                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15441                 break;
15442             case "int":
15443                 cv = function(v){
15444                     return v !== undefined && v !== null && v !== '' ?
15445                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15446                     };
15447                 break;
15448             case "float":
15449                 cv = function(v){
15450                     return v !== undefined && v !== null && v !== '' ?
15451                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15452                     };
15453                 break;
15454             case "bool":
15455             case "boolean":
15456                 cv = function(v){ return v === true || v === "true" || v == 1; };
15457                 break;
15458             case "date":
15459                 cv = function(v){
15460                     if(!v){
15461                         return '';
15462                     }
15463                     if(v instanceof Date){
15464                         return v;
15465                     }
15466                     if(dateFormat){
15467                         if(dateFormat == "timestamp"){
15468                             return new Date(v*1000);
15469                         }
15470                         return Date.parseDate(v, dateFormat);
15471                     }
15472                     var parsed = Date.parse(v);
15473                     return parsed ? new Date(parsed) : null;
15474                 };
15475              break;
15476             
15477         }
15478         this.convert = cv;
15479     }
15480 };
15481
15482 Roo.data.Field.prototype = {
15483     dateFormat: null,
15484     defaultValue: "",
15485     mapping: null,
15486     sortType : null,
15487     sortDir : "ASC"
15488 };/*
15489  * Based on:
15490  * Ext JS Library 1.1.1
15491  * Copyright(c) 2006-2007, Ext JS, LLC.
15492  *
15493  * Originally Released Under LGPL - original licence link has changed is not relivant.
15494  *
15495  * Fork - LGPL
15496  * <script type="text/javascript">
15497  */
15498  
15499 // Base class for reading structured data from a data source.  This class is intended to be
15500 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15501
15502 /**
15503  * @class Roo.data.DataReader
15504  * Base class for reading structured data from a data source.  This class is intended to be
15505  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15506  */
15507
15508 Roo.data.DataReader = function(meta, recordType){
15509     
15510     this.meta = meta;
15511     
15512     this.recordType = recordType instanceof Array ? 
15513         Roo.data.Record.create(recordType) : recordType;
15514 };
15515
15516 Roo.data.DataReader.prototype = {
15517     
15518     
15519     readerType : 'Data',
15520      /**
15521      * Create an empty record
15522      * @param {Object} data (optional) - overlay some values
15523      * @return {Roo.data.Record} record created.
15524      */
15525     newRow :  function(d) {
15526         var da =  {};
15527         this.recordType.prototype.fields.each(function(c) {
15528             switch( c.type) {
15529                 case 'int' : da[c.name] = 0; break;
15530                 case 'date' : da[c.name] = new Date(); break;
15531                 case 'float' : da[c.name] = 0.0; break;
15532                 case 'boolean' : da[c.name] = false; break;
15533                 default : da[c.name] = ""; break;
15534             }
15535             
15536         });
15537         return new this.recordType(Roo.apply(da, d));
15538     }
15539     
15540     
15541 };/*
15542  * Based on:
15543  * Ext JS Library 1.1.1
15544  * Copyright(c) 2006-2007, Ext JS, LLC.
15545  *
15546  * Originally Released Under LGPL - original licence link has changed is not relivant.
15547  *
15548  * Fork - LGPL
15549  * <script type="text/javascript">
15550  */
15551
15552 /**
15553  * @class Roo.data.DataProxy
15554  * @extends Roo.data.Observable
15555  * This class is an abstract base class for implementations which provide retrieval of
15556  * unformatted data objects.<br>
15557  * <p>
15558  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15559  * (of the appropriate type which knows how to parse the data object) to provide a block of
15560  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15561  * <p>
15562  * Custom implementations must implement the load method as described in
15563  * {@link Roo.data.HttpProxy#load}.
15564  */
15565 Roo.data.DataProxy = function(){
15566     this.addEvents({
15567         /**
15568          * @event beforeload
15569          * Fires before a network request is made to retrieve a data object.
15570          * @param {Object} This DataProxy object.
15571          * @param {Object} params The params parameter to the load function.
15572          */
15573         beforeload : true,
15574         /**
15575          * @event load
15576          * Fires before the load method's callback is called.
15577          * @param {Object} This DataProxy object.
15578          * @param {Object} o The data object.
15579          * @param {Object} arg The callback argument object passed to the load function.
15580          */
15581         load : true,
15582         /**
15583          * @event loadexception
15584          * Fires if an Exception occurs during data retrieval.
15585          * @param {Object} This DataProxy object.
15586          * @param {Object} o The data object.
15587          * @param {Object} arg The callback argument object passed to the load function.
15588          * @param {Object} e The Exception.
15589          */
15590         loadexception : true
15591     });
15592     Roo.data.DataProxy.superclass.constructor.call(this);
15593 };
15594
15595 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15596
15597     /**
15598      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15599      */
15600 /*
15601  * Based on:
15602  * Ext JS Library 1.1.1
15603  * Copyright(c) 2006-2007, Ext JS, LLC.
15604  *
15605  * Originally Released Under LGPL - original licence link has changed is not relivant.
15606  *
15607  * Fork - LGPL
15608  * <script type="text/javascript">
15609  */
15610 /**
15611  * @class Roo.data.MemoryProxy
15612  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15613  * to the Reader when its load method is called.
15614  * @constructor
15615  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15616  */
15617 Roo.data.MemoryProxy = function(data){
15618     if (data.data) {
15619         data = data.data;
15620     }
15621     Roo.data.MemoryProxy.superclass.constructor.call(this);
15622     this.data = data;
15623 };
15624
15625 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15626     
15627     /**
15628      * Load data from the requested source (in this case an in-memory
15629      * data object passed to the constructor), read the data object into
15630      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15631      * process that block using the passed callback.
15632      * @param {Object} params This parameter is not used by the MemoryProxy class.
15633      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15634      * object into a block of Roo.data.Records.
15635      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15636      * The function must be passed <ul>
15637      * <li>The Record block object</li>
15638      * <li>The "arg" argument from the load function</li>
15639      * <li>A boolean success indicator</li>
15640      * </ul>
15641      * @param {Object} scope The scope in which to call the callback
15642      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15643      */
15644     load : function(params, reader, callback, scope, arg){
15645         params = params || {};
15646         var result;
15647         try {
15648             result = reader.readRecords(params.data ? params.data :this.data);
15649         }catch(e){
15650             this.fireEvent("loadexception", this, arg, null, e);
15651             callback.call(scope, null, arg, false);
15652             return;
15653         }
15654         callback.call(scope, result, arg, true);
15655     },
15656     
15657     // private
15658     update : function(params, records){
15659         
15660     }
15661 });/*
15662  * Based on:
15663  * Ext JS Library 1.1.1
15664  * Copyright(c) 2006-2007, Ext JS, LLC.
15665  *
15666  * Originally Released Under LGPL - original licence link has changed is not relivant.
15667  *
15668  * Fork - LGPL
15669  * <script type="text/javascript">
15670  */
15671 /**
15672  * @class Roo.data.HttpProxy
15673  * @extends Roo.data.DataProxy
15674  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15675  * configured to reference a certain URL.<br><br>
15676  * <p>
15677  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15678  * from which the running page was served.<br><br>
15679  * <p>
15680  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15681  * <p>
15682  * Be aware that to enable the browser to parse an XML document, the server must set
15683  * the Content-Type header in the HTTP response to "text/xml".
15684  * @constructor
15685  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15686  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15687  * will be used to make the request.
15688  */
15689 Roo.data.HttpProxy = function(conn){
15690     Roo.data.HttpProxy.superclass.constructor.call(this);
15691     // is conn a conn config or a real conn?
15692     this.conn = conn;
15693     this.useAjax = !conn || !conn.events;
15694   
15695 };
15696
15697 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15698     // thse are take from connection...
15699     
15700     /**
15701      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15702      */
15703     /**
15704      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15705      * extra parameters to each request made by this object. (defaults to undefined)
15706      */
15707     /**
15708      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15709      *  to each request made by this object. (defaults to undefined)
15710      */
15711     /**
15712      * @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)
15713      */
15714     /**
15715      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15716      */
15717      /**
15718      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15719      * @type Boolean
15720      */
15721   
15722
15723     /**
15724      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15725      * @type Boolean
15726      */
15727     /**
15728      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15729      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15730      * a finer-grained basis than the DataProxy events.
15731      */
15732     getConnection : function(){
15733         return this.useAjax ? Roo.Ajax : this.conn;
15734     },
15735
15736     /**
15737      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15738      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15739      * process that block using the passed callback.
15740      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15741      * for the request to the remote server.
15742      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15743      * object into a block of Roo.data.Records.
15744      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15745      * The function must be passed <ul>
15746      * <li>The Record block object</li>
15747      * <li>The "arg" argument from the load function</li>
15748      * <li>A boolean success indicator</li>
15749      * </ul>
15750      * @param {Object} scope The scope in which to call the callback
15751      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15752      */
15753     load : function(params, reader, callback, scope, arg){
15754         if(this.fireEvent("beforeload", this, params) !== false){
15755             var  o = {
15756                 params : params || {},
15757                 request: {
15758                     callback : callback,
15759                     scope : scope,
15760                     arg : arg
15761                 },
15762                 reader: reader,
15763                 callback : this.loadResponse,
15764                 scope: this
15765             };
15766             if(this.useAjax){
15767                 Roo.applyIf(o, this.conn);
15768                 if(this.activeRequest){
15769                     Roo.Ajax.abort(this.activeRequest);
15770                 }
15771                 this.activeRequest = Roo.Ajax.request(o);
15772             }else{
15773                 this.conn.request(o);
15774             }
15775         }else{
15776             callback.call(scope||this, null, arg, false);
15777         }
15778     },
15779
15780     // private
15781     loadResponse : function(o, success, response){
15782         delete this.activeRequest;
15783         if(!success){
15784             this.fireEvent("loadexception", this, o, response);
15785             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15786             return;
15787         }
15788         var result;
15789         try {
15790             result = o.reader.read(response);
15791         }catch(e){
15792             this.fireEvent("loadexception", this, o, response, e);
15793             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15794             return;
15795         }
15796         
15797         this.fireEvent("load", this, o, o.request.arg);
15798         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15799     },
15800
15801     // private
15802     update : function(dataSet){
15803
15804     },
15805
15806     // private
15807     updateResponse : function(dataSet){
15808
15809     }
15810 });/*
15811  * Based on:
15812  * Ext JS Library 1.1.1
15813  * Copyright(c) 2006-2007, Ext JS, LLC.
15814  *
15815  * Originally Released Under LGPL - original licence link has changed is not relivant.
15816  *
15817  * Fork - LGPL
15818  * <script type="text/javascript">
15819  */
15820
15821 /**
15822  * @class Roo.data.ScriptTagProxy
15823  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15824  * other than the originating domain of the running page.<br><br>
15825  * <p>
15826  * <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
15827  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15828  * <p>
15829  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15830  * source code that is used as the source inside a &lt;script> tag.<br><br>
15831  * <p>
15832  * In order for the browser to process the returned data, the server must wrap the data object
15833  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15834  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15835  * depending on whether the callback name was passed:
15836  * <p>
15837  * <pre><code>
15838 boolean scriptTag = false;
15839 String cb = request.getParameter("callback");
15840 if (cb != null) {
15841     scriptTag = true;
15842     response.setContentType("text/javascript");
15843 } else {
15844     response.setContentType("application/x-json");
15845 }
15846 Writer out = response.getWriter();
15847 if (scriptTag) {
15848     out.write(cb + "(");
15849 }
15850 out.print(dataBlock.toJsonString());
15851 if (scriptTag) {
15852     out.write(");");
15853 }
15854 </pre></code>
15855  *
15856  * @constructor
15857  * @param {Object} config A configuration object.
15858  */
15859 Roo.data.ScriptTagProxy = function(config){
15860     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15861     Roo.apply(this, config);
15862     this.head = document.getElementsByTagName("head")[0];
15863 };
15864
15865 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15866
15867 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15868     /**
15869      * @cfg {String} url The URL from which to request the data object.
15870      */
15871     /**
15872      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15873      */
15874     timeout : 30000,
15875     /**
15876      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15877      * the server the name of the callback function set up by the load call to process the returned data object.
15878      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15879      * javascript output which calls this named function passing the data object as its only parameter.
15880      */
15881     callbackParam : "callback",
15882     /**
15883      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15884      * name to the request.
15885      */
15886     nocache : true,
15887
15888     /**
15889      * Load data from the configured URL, read the data object into
15890      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15891      * process that block using the passed callback.
15892      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15893      * for the request to the remote server.
15894      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15895      * object into a block of Roo.data.Records.
15896      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15897      * The function must be passed <ul>
15898      * <li>The Record block object</li>
15899      * <li>The "arg" argument from the load function</li>
15900      * <li>A boolean success indicator</li>
15901      * </ul>
15902      * @param {Object} scope The scope in which to call the callback
15903      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15904      */
15905     load : function(params, reader, callback, scope, arg){
15906         if(this.fireEvent("beforeload", this, params) !== false){
15907
15908             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15909
15910             var url = this.url;
15911             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15912             if(this.nocache){
15913                 url += "&_dc=" + (new Date().getTime());
15914             }
15915             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15916             var trans = {
15917                 id : transId,
15918                 cb : "stcCallback"+transId,
15919                 scriptId : "stcScript"+transId,
15920                 params : params,
15921                 arg : arg,
15922                 url : url,
15923                 callback : callback,
15924                 scope : scope,
15925                 reader : reader
15926             };
15927             var conn = this;
15928
15929             window[trans.cb] = function(o){
15930                 conn.handleResponse(o, trans);
15931             };
15932
15933             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15934
15935             if(this.autoAbort !== false){
15936                 this.abort();
15937             }
15938
15939             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15940
15941             var script = document.createElement("script");
15942             script.setAttribute("src", url);
15943             script.setAttribute("type", "text/javascript");
15944             script.setAttribute("id", trans.scriptId);
15945             this.head.appendChild(script);
15946
15947             this.trans = trans;
15948         }else{
15949             callback.call(scope||this, null, arg, false);
15950         }
15951     },
15952
15953     // private
15954     isLoading : function(){
15955         return this.trans ? true : false;
15956     },
15957
15958     /**
15959      * Abort the current server request.
15960      */
15961     abort : function(){
15962         if(this.isLoading()){
15963             this.destroyTrans(this.trans);
15964         }
15965     },
15966
15967     // private
15968     destroyTrans : function(trans, isLoaded){
15969         this.head.removeChild(document.getElementById(trans.scriptId));
15970         clearTimeout(trans.timeoutId);
15971         if(isLoaded){
15972             window[trans.cb] = undefined;
15973             try{
15974                 delete window[trans.cb];
15975             }catch(e){}
15976         }else{
15977             // if hasn't been loaded, wait for load to remove it to prevent script error
15978             window[trans.cb] = function(){
15979                 window[trans.cb] = undefined;
15980                 try{
15981                     delete window[trans.cb];
15982                 }catch(e){}
15983             };
15984         }
15985     },
15986
15987     // private
15988     handleResponse : function(o, trans){
15989         this.trans = false;
15990         this.destroyTrans(trans, true);
15991         var result;
15992         try {
15993             result = trans.reader.readRecords(o);
15994         }catch(e){
15995             this.fireEvent("loadexception", this, o, trans.arg, e);
15996             trans.callback.call(trans.scope||window, null, trans.arg, false);
15997             return;
15998         }
15999         this.fireEvent("load", this, o, trans.arg);
16000         trans.callback.call(trans.scope||window, result, trans.arg, true);
16001     },
16002
16003     // private
16004     handleFailure : function(trans){
16005         this.trans = false;
16006         this.destroyTrans(trans, false);
16007         this.fireEvent("loadexception", this, null, trans.arg);
16008         trans.callback.call(trans.scope||window, null, trans.arg, false);
16009     }
16010 });/*
16011  * Based on:
16012  * Ext JS Library 1.1.1
16013  * Copyright(c) 2006-2007, Ext JS, LLC.
16014  *
16015  * Originally Released Under LGPL - original licence link has changed is not relivant.
16016  *
16017  * Fork - LGPL
16018  * <script type="text/javascript">
16019  */
16020
16021 /**
16022  * @class Roo.data.JsonReader
16023  * @extends Roo.data.DataReader
16024  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16025  * based on mappings in a provided Roo.data.Record constructor.
16026  * 
16027  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16028  * in the reply previously. 
16029  * 
16030  * <p>
16031  * Example code:
16032  * <pre><code>
16033 var RecordDef = Roo.data.Record.create([
16034     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16035     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16036 ]);
16037 var myReader = new Roo.data.JsonReader({
16038     totalProperty: "results",    // The property which contains the total dataset size (optional)
16039     root: "rows",                // The property which contains an Array of row objects
16040     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16041 }, RecordDef);
16042 </code></pre>
16043  * <p>
16044  * This would consume a JSON file like this:
16045  * <pre><code>
16046 { 'results': 2, 'rows': [
16047     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16048     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16049 }
16050 </code></pre>
16051  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16053  * paged from the remote server.
16054  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16055  * @cfg {String} root name of the property which contains the Array of row objects.
16056  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16057  * @cfg {Array} fields Array of field definition objects
16058  * @constructor
16059  * Create a new JsonReader
16060  * @param {Object} meta Metadata configuration options
16061  * @param {Object} recordType Either an Array of field definition objects,
16062  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16063  */
16064 Roo.data.JsonReader = function(meta, recordType){
16065     
16066     meta = meta || {};
16067     // set some defaults:
16068     Roo.applyIf(meta, {
16069         totalProperty: 'total',
16070         successProperty : 'success',
16071         root : 'data',
16072         id : 'id'
16073     });
16074     
16075     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16076 };
16077 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16078     
16079     readerType : 'Json',
16080     
16081     /**
16082      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16083      * Used by Store query builder to append _requestMeta to params.
16084      * 
16085      */
16086     metaFromRemote : false,
16087     /**
16088      * This method is only used by a DataProxy which has retrieved data from a remote server.
16089      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16090      * @return {Object} data A data block which is used by an Roo.data.Store object as
16091      * a cache of Roo.data.Records.
16092      */
16093     read : function(response){
16094         var json = response.responseText;
16095        
16096         var o = /* eval:var:o */ eval("("+json+")");
16097         if(!o) {
16098             throw {message: "JsonReader.read: Json object not found"};
16099         }
16100         
16101         if(o.metaData){
16102             
16103             delete this.ef;
16104             this.metaFromRemote = true;
16105             this.meta = o.metaData;
16106             this.recordType = Roo.data.Record.create(o.metaData.fields);
16107             this.onMetaChange(this.meta, this.recordType, o);
16108         }
16109         return this.readRecords(o);
16110     },
16111
16112     // private function a store will implement
16113     onMetaChange : function(meta, recordType, o){
16114
16115     },
16116
16117     /**
16118          * @ignore
16119          */
16120     simpleAccess: function(obj, subsc) {
16121         return obj[subsc];
16122     },
16123
16124         /**
16125          * @ignore
16126          */
16127     getJsonAccessor: function(){
16128         var re = /[\[\.]/;
16129         return function(expr) {
16130             try {
16131                 return(re.test(expr))
16132                     ? new Function("obj", "return obj." + expr)
16133                     : function(obj){
16134                         return obj[expr];
16135                     };
16136             } catch(e){}
16137             return Roo.emptyFn;
16138         };
16139     }(),
16140
16141     /**
16142      * Create a data block containing Roo.data.Records from an XML document.
16143      * @param {Object} o An object which contains an Array of row objects in the property specified
16144      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16145      * which contains the total size of the dataset.
16146      * @return {Object} data A data block which is used by an Roo.data.Store object as
16147      * a cache of Roo.data.Records.
16148      */
16149     readRecords : function(o){
16150         /**
16151          * After any data loads, the raw JSON data is available for further custom processing.
16152          * @type Object
16153          */
16154         this.o = o;
16155         var s = this.meta, Record = this.recordType,
16156             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16157
16158 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16159         if (!this.ef) {
16160             if(s.totalProperty) {
16161                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16162                 }
16163                 if(s.successProperty) {
16164                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16165                 }
16166                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16167                 if (s.id) {
16168                         var g = this.getJsonAccessor(s.id);
16169                         this.getId = function(rec) {
16170                                 var r = g(rec);  
16171                                 return (r === undefined || r === "") ? null : r;
16172                         };
16173                 } else {
16174                         this.getId = function(){return null;};
16175                 }
16176             this.ef = [];
16177             for(var jj = 0; jj < fl; jj++){
16178                 f = fi[jj];
16179                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16180                 this.ef[jj] = this.getJsonAccessor(map);
16181             }
16182         }
16183
16184         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16185         if(s.totalProperty){
16186             var vt = parseInt(this.getTotal(o), 10);
16187             if(!isNaN(vt)){
16188                 totalRecords = vt;
16189             }
16190         }
16191         if(s.successProperty){
16192             var vs = this.getSuccess(o);
16193             if(vs === false || vs === 'false'){
16194                 success = false;
16195             }
16196         }
16197         var records = [];
16198         for(var i = 0; i < c; i++){
16199                 var n = root[i];
16200             var values = {};
16201             var id = this.getId(n);
16202             for(var j = 0; j < fl; j++){
16203                 f = fi[j];
16204             var v = this.ef[j](n);
16205             if (!f.convert) {
16206                 Roo.log('missing convert for ' + f.name);
16207                 Roo.log(f);
16208                 continue;
16209             }
16210             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16211             }
16212             var record = new Record(values, id);
16213             record.json = n;
16214             records[i] = record;
16215         }
16216         return {
16217             raw : o,
16218             success : success,
16219             records : records,
16220             totalRecords : totalRecords
16221         };
16222     },
16223     // used when loading children.. @see loadDataFromChildren
16224     toLoadData: function(rec)
16225     {
16226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16227         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16228         return { data : data, total : data.length };
16229         
16230     }
16231 });/*
16232  * Based on:
16233  * Ext JS Library 1.1.1
16234  * Copyright(c) 2006-2007, Ext JS, LLC.
16235  *
16236  * Originally Released Under LGPL - original licence link has changed is not relivant.
16237  *
16238  * Fork - LGPL
16239  * <script type="text/javascript">
16240  */
16241
16242 /**
16243  * @class Roo.data.ArrayReader
16244  * @extends Roo.data.DataReader
16245  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16246  * Each element of that Array represents a row of data fields. The
16247  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16248  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16249  * <p>
16250  * Example code:.
16251  * <pre><code>
16252 var RecordDef = Roo.data.Record.create([
16253     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16254     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16255 ]);
16256 var myReader = new Roo.data.ArrayReader({
16257     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16258 }, RecordDef);
16259 </code></pre>
16260  * <p>
16261  * This would consume an Array like this:
16262  * <pre><code>
16263 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16264   </code></pre>
16265  
16266  * @constructor
16267  * Create a new JsonReader
16268  * @param {Object} meta Metadata configuration options.
16269  * @param {Object|Array} recordType Either an Array of field definition objects
16270  * 
16271  * @cfg {Array} fields Array of field definition objects
16272  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16273  * as specified to {@link Roo.data.Record#create},
16274  * or an {@link Roo.data.Record} object
16275  *
16276  * 
16277  * created using {@link Roo.data.Record#create}.
16278  */
16279 Roo.data.ArrayReader = function(meta, recordType)
16280 {    
16281     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16282 };
16283
16284 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16285     
16286       /**
16287      * Create a data block containing Roo.data.Records from an XML document.
16288      * @param {Object} o An Array of row objects which represents the dataset.
16289      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16290      * a cache of Roo.data.Records.
16291      */
16292     readRecords : function(o)
16293     {
16294         var sid = this.meta ? this.meta.id : null;
16295         var recordType = this.recordType, fields = recordType.prototype.fields;
16296         var records = [];
16297         var root = o;
16298         for(var i = 0; i < root.length; i++){
16299             var n = root[i];
16300             var values = {};
16301             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16302             for(var j = 0, jlen = fields.length; j < jlen; j++){
16303                 var f = fields.items[j];
16304                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16305                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16306                 v = f.convert(v);
16307                 values[f.name] = v;
16308             }
16309             var record = new recordType(values, id);
16310             record.json = n;
16311             records[records.length] = record;
16312         }
16313         return {
16314             records : records,
16315             totalRecords : records.length
16316         };
16317     },
16318     // used when loading children.. @see loadDataFromChildren
16319     toLoadData: function(rec)
16320     {
16321         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16322         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16323         
16324     }
16325     
16326     
16327 });/*
16328  * - LGPL
16329  * * 
16330  */
16331
16332 /**
16333  * @class Roo.bootstrap.ComboBox
16334  * @extends Roo.bootstrap.TriggerField
16335  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16336  * @cfg {Boolean} append (true|false) default false
16337  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16338  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16339  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16340  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16341  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16342  * @cfg {Boolean} animate default true
16343  * @cfg {Boolean} emptyResultText only for touch device
16344  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16345  * @cfg {String} emptyTitle default ''
16346  * @cfg {Number} width fixed with? experimental
16347  * @constructor
16348  * Create a new ComboBox.
16349  * @param {Object} config Configuration options
16350  */
16351 Roo.bootstrap.ComboBox = function(config){
16352     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16353     this.addEvents({
16354         /**
16355          * @event expand
16356          * Fires when the dropdown list is expanded
16357         * @param {Roo.bootstrap.ComboBox} combo This combo box
16358         */
16359         'expand' : true,
16360         /**
16361          * @event collapse
16362          * Fires when the dropdown list is collapsed
16363         * @param {Roo.bootstrap.ComboBox} combo This combo box
16364         */
16365         'collapse' : true,
16366         /**
16367          * @event beforeselect
16368          * Fires before a list item is selected. Return false to cancel the selection.
16369         * @param {Roo.bootstrap.ComboBox} combo This combo box
16370         * @param {Roo.data.Record} record The data record returned from the underlying store
16371         * @param {Number} index The index of the selected item in the dropdown list
16372         */
16373         'beforeselect' : true,
16374         /**
16375          * @event select
16376          * Fires when a list item is selected
16377         * @param {Roo.bootstrap.ComboBox} combo This combo box
16378         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16379         * @param {Number} index The index of the selected item in the dropdown list
16380         */
16381         'select' : true,
16382         /**
16383          * @event beforequery
16384          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16385          * The event object passed has these properties:
16386         * @param {Roo.bootstrap.ComboBox} combo This combo box
16387         * @param {String} query The query
16388         * @param {Boolean} forceAll true to force "all" query
16389         * @param {Boolean} cancel true to cancel the query
16390         * @param {Object} e The query event object
16391         */
16392         'beforequery': true,
16393          /**
16394          * @event add
16395          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16396         * @param {Roo.bootstrap.ComboBox} combo This combo box
16397         */
16398         'add' : true,
16399         /**
16400          * @event edit
16401          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16402         * @param {Roo.bootstrap.ComboBox} combo This combo box
16403         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16404         */
16405         'edit' : true,
16406         /**
16407          * @event remove
16408          * Fires when the remove value from the combobox array
16409         * @param {Roo.bootstrap.ComboBox} combo This combo box
16410         */
16411         'remove' : true,
16412         /**
16413          * @event afterremove
16414          * Fires when the remove value from the combobox array
16415         * @param {Roo.bootstrap.ComboBox} combo This combo box
16416         */
16417         'afterremove' : true,
16418         /**
16419          * @event specialfilter
16420          * Fires when specialfilter
16421             * @param {Roo.bootstrap.ComboBox} combo This combo box
16422             */
16423         'specialfilter' : true,
16424         /**
16425          * @event tick
16426          * Fires when tick the element
16427             * @param {Roo.bootstrap.ComboBox} combo This combo box
16428             */
16429         'tick' : true,
16430         /**
16431          * @event touchviewdisplay
16432          * Fires when touch view require special display (default is using displayField)
16433             * @param {Roo.bootstrap.ComboBox} combo This combo box
16434             * @param {Object} cfg set html .
16435             */
16436         'touchviewdisplay' : true
16437         
16438     });
16439     
16440     this.item = [];
16441     this.tickItems = [];
16442     
16443     this.selectedIndex = -1;
16444     if(this.mode == 'local'){
16445         if(config.queryDelay === undefined){
16446             this.queryDelay = 10;
16447         }
16448         if(config.minChars === undefined){
16449             this.minChars = 0;
16450         }
16451     }
16452 };
16453
16454 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16455      
16456     /**
16457      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16458      * rendering into an Roo.Editor, defaults to false)
16459      */
16460     /**
16461      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16462      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16463      */
16464     /**
16465      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16466      */
16467     /**
16468      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16469      * the dropdown list (defaults to undefined, with no header element)
16470      */
16471
16472      /**
16473      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16474      */
16475      
16476      /**
16477      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16478      */
16479     listWidth: undefined,
16480     /**
16481      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16482      * mode = 'remote' or 'text' if mode = 'local')
16483      */
16484     displayField: undefined,
16485     
16486     /**
16487      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16488      * mode = 'remote' or 'value' if mode = 'local'). 
16489      * Note: use of a valueField requires the user make a selection
16490      * in order for a value to be mapped.
16491      */
16492     valueField: undefined,
16493     /**
16494      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16495      */
16496     modalTitle : '',
16497     
16498     /**
16499      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16500      * field's data value (defaults to the underlying DOM element's name)
16501      */
16502     hiddenName: undefined,
16503     /**
16504      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16505      */
16506     listClass: '',
16507     /**
16508      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16509      */
16510     selectedClass: 'active',
16511     
16512     /**
16513      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16514      */
16515     shadow:'sides',
16516     /**
16517      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16518      * anchor positions (defaults to 'tl-bl')
16519      */
16520     listAlign: 'tl-bl?',
16521     /**
16522      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16523      */
16524     maxHeight: 300,
16525     /**
16526      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16527      * query specified by the allQuery config option (defaults to 'query')
16528      */
16529     triggerAction: 'query',
16530     /**
16531      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16532      * (defaults to 4, does not apply if editable = false)
16533      */
16534     minChars : 4,
16535     /**
16536      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16537      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16538      */
16539     typeAhead: false,
16540     /**
16541      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16542      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16543      */
16544     queryDelay: 500,
16545     /**
16546      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16547      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16548      */
16549     pageSize: 0,
16550     /**
16551      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16552      * when editable = true (defaults to false)
16553      */
16554     selectOnFocus:false,
16555     /**
16556      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16557      */
16558     queryParam: 'query',
16559     /**
16560      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16561      * when mode = 'remote' (defaults to 'Loading...')
16562      */
16563     loadingText: 'Loading...',
16564     /**
16565      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16566      */
16567     resizable: false,
16568     /**
16569      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16570      */
16571     handleHeight : 8,
16572     /**
16573      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16574      * traditional select (defaults to true)
16575      */
16576     editable: true,
16577     /**
16578      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16579      */
16580     allQuery: '',
16581     /**
16582      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16583      */
16584     mode: 'remote',
16585     /**
16586      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16587      * listWidth has a higher value)
16588      */
16589     minListWidth : 70,
16590     /**
16591      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16592      * allow the user to set arbitrary text into the field (defaults to false)
16593      */
16594     forceSelection:false,
16595     /**
16596      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16597      * if typeAhead = true (defaults to 250)
16598      */
16599     typeAheadDelay : 250,
16600     /**
16601      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16602      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16603      */
16604     valueNotFoundText : undefined,
16605     /**
16606      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16607      */
16608     blockFocus : false,
16609     
16610     /**
16611      * @cfg {Boolean} disableClear Disable showing of clear button.
16612      */
16613     disableClear : false,
16614     /**
16615      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16616      */
16617     alwaysQuery : false,
16618     
16619     /**
16620      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16621      */
16622     multiple : false,
16623     
16624     /**
16625      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16626      */
16627     invalidClass : "has-warning",
16628     
16629     /**
16630      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16631      */
16632     validClass : "has-success",
16633     
16634     /**
16635      * @cfg {Boolean} specialFilter (true|false) special filter default false
16636      */
16637     specialFilter : false,
16638     
16639     /**
16640      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16641      */
16642     mobileTouchView : true,
16643     
16644     /**
16645      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16646      */
16647     useNativeIOS : false,
16648     
16649     /**
16650      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16651      */
16652     mobile_restrict_height : false,
16653     
16654     ios_options : false,
16655     
16656     //private
16657     addicon : false,
16658     editicon: false,
16659     
16660     page: 0,
16661     hasQuery: false,
16662     append: false,
16663     loadNext: false,
16664     autoFocus : true,
16665     tickable : false,
16666     btnPosition : 'right',
16667     triggerList : true,
16668     showToggleBtn : true,
16669     animate : true,
16670     emptyResultText: 'Empty',
16671     triggerText : 'Select',
16672     emptyTitle : '',
16673     width : false,
16674     
16675     // element that contains real text value.. (when hidden is used..)
16676     
16677     getAutoCreate : function()
16678     {   
16679         var cfg = false;
16680         //render
16681         /*
16682          * Render classic select for iso
16683          */
16684         
16685         if(Roo.isIOS && this.useNativeIOS){
16686             cfg = this.getAutoCreateNativeIOS();
16687             return cfg;
16688         }
16689         
16690         /*
16691          * Touch Devices
16692          */
16693         
16694         if(Roo.isTouch && this.mobileTouchView){
16695             cfg = this.getAutoCreateTouchView();
16696             return cfg;;
16697         }
16698         
16699         /*
16700          *  Normal ComboBox
16701          */
16702         if(!this.tickable){
16703             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16704             return cfg;
16705         }
16706         
16707         /*
16708          *  ComboBox with tickable selections
16709          */
16710              
16711         var align = this.labelAlign || this.parentLabelAlign();
16712         
16713         cfg = {
16714             cls : 'form-group roo-combobox-tickable' //input-group
16715         };
16716         
16717         var btn_text_select = '';
16718         var btn_text_done = '';
16719         var btn_text_cancel = '';
16720         
16721         if (this.btn_text_show) {
16722             btn_text_select = 'Select';
16723             btn_text_done = 'Done';
16724             btn_text_cancel = 'Cancel'; 
16725         }
16726         
16727         var buttons = {
16728             tag : 'div',
16729             cls : 'tickable-buttons',
16730             cn : [
16731                 {
16732                     tag : 'button',
16733                     type : 'button',
16734                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16735                     //html : this.triggerText
16736                     html: btn_text_select
16737                 },
16738                 {
16739                     tag : 'button',
16740                     type : 'button',
16741                     name : 'ok',
16742                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16743                     //html : 'Done'
16744                     html: btn_text_done
16745                 },
16746                 {
16747                     tag : 'button',
16748                     type : 'button',
16749                     name : 'cancel',
16750                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16751                     //html : 'Cancel'
16752                     html: btn_text_cancel
16753                 }
16754             ]
16755         };
16756         
16757         if(this.editable){
16758             buttons.cn.unshift({
16759                 tag: 'input',
16760                 cls: 'roo-select2-search-field-input'
16761             });
16762         }
16763         
16764         var _this = this;
16765         
16766         Roo.each(buttons.cn, function(c){
16767             if (_this.size) {
16768                 c.cls += ' btn-' + _this.size;
16769             }
16770
16771             if (_this.disabled) {
16772                 c.disabled = true;
16773             }
16774         });
16775         
16776         var box = {
16777             tag: 'div',
16778             style : 'display: contents',
16779             cn: [
16780                 {
16781                     tag: 'input',
16782                     type : 'hidden',
16783                     cls: 'form-hidden-field'
16784                 },
16785                 {
16786                     tag: 'ul',
16787                     cls: 'roo-select2-choices',
16788                     cn:[
16789                         {
16790                             tag: 'li',
16791                             cls: 'roo-select2-search-field',
16792                             cn: [
16793                                 buttons
16794                             ]
16795                         }
16796                     ]
16797                 }
16798             ]
16799         };
16800         
16801         var combobox = {
16802             cls: 'roo-select2-container input-group roo-select2-container-multi',
16803             cn: [
16804                 
16805                 box
16806 //                {
16807 //                    tag: 'ul',
16808 //                    cls: 'typeahead typeahead-long dropdown-menu',
16809 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16810 //                }
16811             ]
16812         };
16813         
16814         if(this.hasFeedback && !this.allowBlank){
16815             
16816             var feedback = {
16817                 tag: 'span',
16818                 cls: 'glyphicon form-control-feedback'
16819             };
16820
16821             combobox.cn.push(feedback);
16822         }
16823         
16824         
16825         
16826         var indicator = {
16827             tag : 'i',
16828             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16829             tooltip : 'This field is required'
16830         };
16831         if (Roo.bootstrap.version == 4) {
16832             indicator = {
16833                 tag : 'i',
16834                 style : 'display:none'
16835             };
16836         }
16837         if (align ==='left' && this.fieldLabel.length) {
16838             
16839             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16840             
16841             cfg.cn = [
16842                 indicator,
16843                 {
16844                     tag: 'label',
16845                     'for' :  id,
16846                     cls : 'control-label col-form-label',
16847                     html : this.fieldLabel
16848
16849                 },
16850                 {
16851                     cls : "", 
16852                     cn: [
16853                         combobox
16854                     ]
16855                 }
16856
16857             ];
16858             
16859             var labelCfg = cfg.cn[1];
16860             var contentCfg = cfg.cn[2];
16861             
16862
16863             if(this.indicatorpos == 'right'){
16864                 
16865                 cfg.cn = [
16866                     {
16867                         tag: 'label',
16868                         'for' :  id,
16869                         cls : 'control-label col-form-label',
16870                         cn : [
16871                             {
16872                                 tag : 'span',
16873                                 html : this.fieldLabel
16874                             },
16875                             indicator
16876                         ]
16877                     },
16878                     {
16879                         cls : "",
16880                         cn: [
16881                             combobox
16882                         ]
16883                     }
16884
16885                 ];
16886                 
16887                 
16888                 
16889                 labelCfg = cfg.cn[0];
16890                 contentCfg = cfg.cn[1];
16891             
16892             }
16893             
16894             if(this.labelWidth > 12){
16895                 labelCfg.style = "width: " + this.labelWidth + 'px';
16896             }
16897             if(this.width * 1 > 0){
16898                 contentCfg.style = "width: " + this.width + 'px';
16899             }
16900             if(this.labelWidth < 13 && this.labelmd == 0){
16901                 this.labelmd = this.labelWidth;
16902             }
16903             
16904             if(this.labellg > 0){
16905                 labelCfg.cls += ' col-lg-' + this.labellg;
16906                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16907             }
16908             
16909             if(this.labelmd > 0){
16910                 labelCfg.cls += ' col-md-' + this.labelmd;
16911                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16912             }
16913             
16914             if(this.labelsm > 0){
16915                 labelCfg.cls += ' col-sm-' + this.labelsm;
16916                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16917             }
16918             
16919             if(this.labelxs > 0){
16920                 labelCfg.cls += ' col-xs-' + this.labelxs;
16921                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16922             }
16923                 
16924                 
16925         } else if ( this.fieldLabel.length) {
16926 //                Roo.log(" label");
16927                  cfg.cn = [
16928                    indicator,
16929                     {
16930                         tag: 'label',
16931                         //cls : 'input-group-addon',
16932                         html : this.fieldLabel
16933                     },
16934                     combobox
16935                 ];
16936                 
16937                 if(this.indicatorpos == 'right'){
16938                     cfg.cn = [
16939                         {
16940                             tag: 'label',
16941                             //cls : 'input-group-addon',
16942                             html : this.fieldLabel
16943                         },
16944                         indicator,
16945                         combobox
16946                     ];
16947                     
16948                 }
16949
16950         } else {
16951             
16952 //                Roo.log(" no label && no align");
16953                 cfg = combobox
16954                      
16955                 
16956         }
16957          
16958         var settings=this;
16959         ['xs','sm','md','lg'].map(function(size){
16960             if (settings[size]) {
16961                 cfg.cls += ' col-' + size + '-' + settings[size];
16962             }
16963         });
16964         
16965         return cfg;
16966         
16967     },
16968     
16969     _initEventsCalled : false,
16970     
16971     // private
16972     initEvents: function()
16973     {   
16974         if (this._initEventsCalled) { // as we call render... prevent looping...
16975             return;
16976         }
16977         this._initEventsCalled = true;
16978         
16979         if (!this.store) {
16980             throw "can not find store for combo";
16981         }
16982         
16983         this.indicator = this.indicatorEl();
16984         
16985         this.store = Roo.factory(this.store, Roo.data);
16986         this.store.parent = this;
16987         
16988         // if we are building from html. then this element is so complex, that we can not really
16989         // use the rendered HTML.
16990         // so we have to trash and replace the previous code.
16991         if (Roo.XComponent.build_from_html) {
16992             // remove this element....
16993             var e = this.el.dom, k=0;
16994             while (e ) { e = e.previousSibling;  ++k;}
16995
16996             this.el.remove();
16997             
16998             this.el=false;
16999             this.rendered = false;
17000             
17001             this.render(this.parent().getChildContainer(true), k);
17002         }
17003         
17004         if(Roo.isIOS && this.useNativeIOS){
17005             this.initIOSView();
17006             return;
17007         }
17008         
17009         /*
17010          * Touch Devices
17011          */
17012         
17013         if(Roo.isTouch && this.mobileTouchView){
17014             this.initTouchView();
17015             return;
17016         }
17017         
17018         if(this.tickable){
17019             this.initTickableEvents();
17020             return;
17021         }
17022         
17023         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17024         
17025         if(this.hiddenName){
17026             
17027             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17028             
17029             this.hiddenField.dom.value =
17030                 this.hiddenValue !== undefined ? this.hiddenValue :
17031                 this.value !== undefined ? this.value : '';
17032
17033             // prevent input submission
17034             this.el.dom.removeAttribute('name');
17035             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17036              
17037              
17038         }
17039         //if(Roo.isGecko){
17040         //    this.el.dom.setAttribute('autocomplete', 'off');
17041         //}
17042         
17043         var cls = 'x-combo-list';
17044         
17045         //this.list = new Roo.Layer({
17046         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17047         //});
17048         
17049         var _this = this;
17050         
17051         (function(){
17052             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17053             _this.list.setWidth(lw);
17054         }).defer(100);
17055         
17056         this.list.on('mouseover', this.onViewOver, this);
17057         this.list.on('mousemove', this.onViewMove, this);
17058         this.list.on('scroll', this.onViewScroll, this);
17059         
17060         /*
17061         this.list.swallowEvent('mousewheel');
17062         this.assetHeight = 0;
17063
17064         if(this.title){
17065             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17066             this.assetHeight += this.header.getHeight();
17067         }
17068
17069         this.innerList = this.list.createChild({cls:cls+'-inner'});
17070         this.innerList.on('mouseover', this.onViewOver, this);
17071         this.innerList.on('mousemove', this.onViewMove, this);
17072         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17073         
17074         if(this.allowBlank && !this.pageSize && !this.disableClear){
17075             this.footer = this.list.createChild({cls:cls+'-ft'});
17076             this.pageTb = new Roo.Toolbar(this.footer);
17077            
17078         }
17079         if(this.pageSize){
17080             this.footer = this.list.createChild({cls:cls+'-ft'});
17081             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17082                     {pageSize: this.pageSize});
17083             
17084         }
17085         
17086         if (this.pageTb && this.allowBlank && !this.disableClear) {
17087             var _this = this;
17088             this.pageTb.add(new Roo.Toolbar.Fill(), {
17089                 cls: 'x-btn-icon x-btn-clear',
17090                 text: '&#160;',
17091                 handler: function()
17092                 {
17093                     _this.collapse();
17094                     _this.clearValue();
17095                     _this.onSelect(false, -1);
17096                 }
17097             });
17098         }
17099         if (this.footer) {
17100             this.assetHeight += this.footer.getHeight();
17101         }
17102         */
17103             
17104         if(!this.tpl){
17105             this.tpl = Roo.bootstrap.version == 4 ?
17106                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17107                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17108         }
17109
17110         this.view = new Roo.View(this.list, this.tpl, {
17111             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17112         });
17113         //this.view.wrapEl.setDisplayed(false);
17114         this.view.on('click', this.onViewClick, this);
17115         
17116         
17117         this.store.on('beforeload', this.onBeforeLoad, this);
17118         this.store.on('load', this.onLoad, this);
17119         this.store.on('loadexception', this.onLoadException, this);
17120         /*
17121         if(this.resizable){
17122             this.resizer = new Roo.Resizable(this.list,  {
17123                pinned:true, handles:'se'
17124             });
17125             this.resizer.on('resize', function(r, w, h){
17126                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17127                 this.listWidth = w;
17128                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17129                 this.restrictHeight();
17130             }, this);
17131             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17132         }
17133         */
17134         if(!this.editable){
17135             this.editable = true;
17136             this.setEditable(false);
17137         }
17138         
17139         /*
17140         
17141         if (typeof(this.events.add.listeners) != 'undefined') {
17142             
17143             this.addicon = this.wrap.createChild(
17144                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17145        
17146             this.addicon.on('click', function(e) {
17147                 this.fireEvent('add', this);
17148             }, this);
17149         }
17150         if (typeof(this.events.edit.listeners) != 'undefined') {
17151             
17152             this.editicon = this.wrap.createChild(
17153                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17154             if (this.addicon) {
17155                 this.editicon.setStyle('margin-left', '40px');
17156             }
17157             this.editicon.on('click', function(e) {
17158                 
17159                 // we fire even  if inothing is selected..
17160                 this.fireEvent('edit', this, this.lastData );
17161                 
17162             }, this);
17163         }
17164         */
17165         
17166         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17167             "up" : function(e){
17168                 this.inKeyMode = true;
17169                 this.selectPrev();
17170             },
17171
17172             "down" : function(e){
17173                 if(!this.isExpanded()){
17174                     this.onTriggerClick();
17175                 }else{
17176                     this.inKeyMode = true;
17177                     this.selectNext();
17178                 }
17179             },
17180
17181             "enter" : function(e){
17182 //                this.onViewClick();
17183                 //return true;
17184                 this.collapse();
17185                 
17186                 if(this.fireEvent("specialkey", this, e)){
17187                     this.onViewClick(false);
17188                 }
17189                 
17190                 return true;
17191             },
17192
17193             "esc" : function(e){
17194                 this.collapse();
17195             },
17196
17197             "tab" : function(e){
17198                 this.collapse();
17199                 
17200                 if(this.fireEvent("specialkey", this, e)){
17201                     this.onViewClick(false);
17202                 }
17203                 
17204                 return true;
17205             },
17206
17207             scope : this,
17208
17209             doRelay : function(foo, bar, hname){
17210                 if(hname == 'down' || this.scope.isExpanded()){
17211                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17212                 }
17213                 return true;
17214             },
17215
17216             forceKeyDown: true
17217         });
17218         
17219         
17220         this.queryDelay = Math.max(this.queryDelay || 10,
17221                 this.mode == 'local' ? 10 : 250);
17222         
17223         
17224         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17225         
17226         if(this.typeAhead){
17227             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17228         }
17229         if(this.editable !== false){
17230             this.inputEl().on("keyup", this.onKeyUp, this);
17231         }
17232         if(this.forceSelection){
17233             this.inputEl().on('blur', this.doForce, this);
17234         }
17235         
17236         if(this.multiple){
17237             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17238             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17239         }
17240     },
17241     
17242     initTickableEvents: function()
17243     {   
17244         this.createList();
17245         
17246         if(this.hiddenName){
17247             
17248             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17249             
17250             this.hiddenField.dom.value =
17251                 this.hiddenValue !== undefined ? this.hiddenValue :
17252                 this.value !== undefined ? this.value : '';
17253
17254             // prevent input submission
17255             this.el.dom.removeAttribute('name');
17256             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17257              
17258              
17259         }
17260         
17261 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17262         
17263         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17264         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17265         if(this.triggerList){
17266             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17267         }
17268          
17269         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17270         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17271         
17272         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17273         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17274         
17275         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17276         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17277         
17278         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17279         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17280         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17281         
17282         this.okBtn.hide();
17283         this.cancelBtn.hide();
17284         
17285         var _this = this;
17286         
17287         (function(){
17288             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17289             _this.list.setWidth(lw);
17290         }).defer(100);
17291         
17292         this.list.on('mouseover', this.onViewOver, this);
17293         this.list.on('mousemove', this.onViewMove, this);
17294         
17295         this.list.on('scroll', this.onViewScroll, this);
17296         
17297         if(!this.tpl){
17298             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17299                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17300         }
17301
17302         this.view = new Roo.View(this.list, this.tpl, {
17303             singleSelect:true,
17304             tickable:true,
17305             parent:this,
17306             store: this.store,
17307             selectedClass: this.selectedClass
17308         });
17309         
17310         //this.view.wrapEl.setDisplayed(false);
17311         this.view.on('click', this.onViewClick, this);
17312         
17313         
17314         
17315         this.store.on('beforeload', this.onBeforeLoad, this);
17316         this.store.on('load', this.onLoad, this);
17317         this.store.on('loadexception', this.onLoadException, this);
17318         
17319         if(this.editable){
17320             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17321                 "up" : function(e){
17322                     this.inKeyMode = true;
17323                     this.selectPrev();
17324                 },
17325
17326                 "down" : function(e){
17327                     this.inKeyMode = true;
17328                     this.selectNext();
17329                 },
17330
17331                 "enter" : function(e){
17332                     if(this.fireEvent("specialkey", this, e)){
17333                         this.onViewClick(false);
17334                     }
17335                     
17336                     return true;
17337                 },
17338
17339                 "esc" : function(e){
17340                     this.onTickableFooterButtonClick(e, false, false);
17341                 },
17342
17343                 "tab" : function(e){
17344                     this.fireEvent("specialkey", this, e);
17345                     
17346                     this.onTickableFooterButtonClick(e, false, false);
17347                     
17348                     return true;
17349                 },
17350
17351                 scope : this,
17352
17353                 doRelay : function(e, fn, key){
17354                     if(this.scope.isExpanded()){
17355                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17356                     }
17357                     return true;
17358                 },
17359
17360                 forceKeyDown: true
17361             });
17362         }
17363         
17364         this.queryDelay = Math.max(this.queryDelay || 10,
17365                 this.mode == 'local' ? 10 : 250);
17366         
17367         
17368         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17369         
17370         if(this.typeAhead){
17371             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17372         }
17373         
17374         if(this.editable !== false){
17375             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17376         }
17377         
17378         this.indicator = this.indicatorEl();
17379         
17380         if(this.indicator){
17381             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17382             this.indicator.hide();
17383         }
17384         
17385     },
17386
17387     onDestroy : function(){
17388         if(this.view){
17389             this.view.setStore(null);
17390             this.view.el.removeAllListeners();
17391             this.view.el.remove();
17392             this.view.purgeListeners();
17393         }
17394         if(this.list){
17395             this.list.dom.innerHTML  = '';
17396         }
17397         
17398         if(this.store){
17399             this.store.un('beforeload', this.onBeforeLoad, this);
17400             this.store.un('load', this.onLoad, this);
17401             this.store.un('loadexception', this.onLoadException, this);
17402         }
17403         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17404     },
17405
17406     // private
17407     fireKey : function(e){
17408         if(e.isNavKeyPress() && !this.list.isVisible()){
17409             this.fireEvent("specialkey", this, e);
17410         }
17411     },
17412
17413     // private
17414     onResize: function(w, h)
17415     {
17416         
17417         
17418 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17419 //        
17420 //        if(typeof w != 'number'){
17421 //            // we do not handle it!?!?
17422 //            return;
17423 //        }
17424 //        var tw = this.trigger.getWidth();
17425 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17426 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17427 //        var x = w - tw;
17428 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17429 //            
17430 //        //this.trigger.setStyle('left', x+'px');
17431 //        
17432 //        if(this.list && this.listWidth === undefined){
17433 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17434 //            this.list.setWidth(lw);
17435 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17436 //        }
17437         
17438     
17439         
17440     },
17441
17442     /**
17443      * Allow or prevent the user from directly editing the field text.  If false is passed,
17444      * the user will only be able to select from the items defined in the dropdown list.  This method
17445      * is the runtime equivalent of setting the 'editable' config option at config time.
17446      * @param {Boolean} value True to allow the user to directly edit the field text
17447      */
17448     setEditable : function(value){
17449         if(value == this.editable){
17450             return;
17451         }
17452         this.editable = value;
17453         if(!value){
17454             this.inputEl().dom.setAttribute('readOnly', true);
17455             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17456             this.inputEl().addClass('x-combo-noedit');
17457         }else{
17458             this.inputEl().dom.removeAttribute('readOnly');
17459             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17460             this.inputEl().removeClass('x-combo-noedit');
17461         }
17462     },
17463
17464     // private
17465     
17466     onBeforeLoad : function(combo,opts){
17467         if(!this.hasFocus){
17468             return;
17469         }
17470          if (!opts.add) {
17471             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17472          }
17473         this.restrictHeight();
17474         this.selectedIndex = -1;
17475     },
17476
17477     // private
17478     onLoad : function(){
17479         
17480         this.hasQuery = false;
17481         
17482         if(!this.hasFocus){
17483             return;
17484         }
17485         
17486         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17487             this.loading.hide();
17488         }
17489         
17490         if(this.store.getCount() > 0){
17491             
17492             this.expand();
17493             this.restrictHeight();
17494             if(this.lastQuery == this.allQuery){
17495                 if(this.editable && !this.tickable){
17496                     this.inputEl().dom.select();
17497                 }
17498                 
17499                 if(
17500                     !this.selectByValue(this.value, true) &&
17501                     this.autoFocus && 
17502                     (
17503                         !this.store.lastOptions ||
17504                         typeof(this.store.lastOptions.add) == 'undefined' || 
17505                         this.store.lastOptions.add != true
17506                     )
17507                 ){
17508                     this.select(0, true);
17509                 }
17510             }else{
17511                 if(this.autoFocus){
17512                     this.selectNext();
17513                 }
17514                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17515                     this.taTask.delay(this.typeAheadDelay);
17516                 }
17517             }
17518         }else{
17519             this.onEmptyResults();
17520         }
17521         
17522         //this.el.focus();
17523     },
17524     // private
17525     onLoadException : function()
17526     {
17527         this.hasQuery = false;
17528         
17529         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17530             this.loading.hide();
17531         }
17532         
17533         if(this.tickable && this.editable){
17534             return;
17535         }
17536         
17537         this.collapse();
17538         // only causes errors at present
17539         //Roo.log(this.store.reader.jsonData);
17540         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17541             // fixme
17542             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17543         //}
17544         
17545         
17546     },
17547     // private
17548     onTypeAhead : function(){
17549         if(this.store.getCount() > 0){
17550             var r = this.store.getAt(0);
17551             var newValue = r.data[this.displayField];
17552             var len = newValue.length;
17553             var selStart = this.getRawValue().length;
17554             
17555             if(selStart != len){
17556                 this.setRawValue(newValue);
17557                 this.selectText(selStart, newValue.length);
17558             }
17559         }
17560     },
17561
17562     // private
17563     onSelect : function(record, index){
17564         
17565         if(this.fireEvent('beforeselect', this, record, index) !== false){
17566         
17567             this.setFromData(index > -1 ? record.data : false);
17568             
17569             this.collapse();
17570             this.fireEvent('select', this, record, index);
17571         }
17572     },
17573
17574     /**
17575      * Returns the currently selected field value or empty string if no value is set.
17576      * @return {String} value The selected value
17577      */
17578     getValue : function()
17579     {
17580         if(Roo.isIOS && this.useNativeIOS){
17581             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17582         }
17583         
17584         if(this.multiple){
17585             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17586         }
17587         
17588         if(this.valueField){
17589             return typeof this.value != 'undefined' ? this.value : '';
17590         }else{
17591             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17592         }
17593     },
17594     
17595     getRawValue : function()
17596     {
17597         if(Roo.isIOS && this.useNativeIOS){
17598             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17599         }
17600         
17601         var v = this.inputEl().getValue();
17602         
17603         return v;
17604     },
17605
17606     /**
17607      * Clears any text/value currently set in the field
17608      */
17609     clearValue : function(){
17610         
17611         if(this.hiddenField){
17612             this.hiddenField.dom.value = '';
17613         }
17614         this.value = '';
17615         this.setRawValue('');
17616         this.lastSelectionText = '';
17617         this.lastData = false;
17618         
17619         var close = this.closeTriggerEl();
17620         
17621         if(close){
17622             close.hide();
17623         }
17624         
17625         this.validate();
17626         
17627     },
17628
17629     /**
17630      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17631      * will be displayed in the field.  If the value does not match the data value of an existing item,
17632      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17633      * Otherwise the field will be blank (although the value will still be set).
17634      * @param {String} value The value to match
17635      */
17636     setValue : function(v)
17637     {
17638         if(Roo.isIOS && this.useNativeIOS){
17639             this.setIOSValue(v);
17640             return;
17641         }
17642         
17643         if(this.multiple){
17644             this.syncValue();
17645             return;
17646         }
17647         
17648         var text = v;
17649         if(this.valueField){
17650             var r = this.findRecord(this.valueField, v);
17651             if(r){
17652                 text = r.data[this.displayField];
17653             }else if(this.valueNotFoundText !== undefined){
17654                 text = this.valueNotFoundText;
17655             }
17656         }
17657         this.lastSelectionText = text;
17658         if(this.hiddenField){
17659             this.hiddenField.dom.value = v;
17660         }
17661         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17662         this.value = v;
17663         
17664         var close = this.closeTriggerEl();
17665         
17666         if(close){
17667             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17668         }
17669         
17670         this.validate();
17671     },
17672     /**
17673      * @property {Object} the last set data for the element
17674      */
17675     
17676     lastData : false,
17677     /**
17678      * Sets the value of the field based on a object which is related to the record format for the store.
17679      * @param {Object} value the value to set as. or false on reset?
17680      */
17681     setFromData : function(o){
17682         
17683         if(this.multiple){
17684             this.addItem(o);
17685             return;
17686         }
17687             
17688         var dv = ''; // display value
17689         var vv = ''; // value value..
17690         this.lastData = o;
17691         if (this.displayField) {
17692             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17693         } else {
17694             // this is an error condition!!!
17695             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17696         }
17697         
17698         if(this.valueField){
17699             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17700         }
17701         
17702         var close = this.closeTriggerEl();
17703         
17704         if(close){
17705             if(dv.length || vv * 1 > 0){
17706                 close.show() ;
17707                 this.blockFocus=true;
17708             } else {
17709                 close.hide();
17710             }             
17711         }
17712         
17713         if(this.hiddenField){
17714             this.hiddenField.dom.value = vv;
17715             
17716             this.lastSelectionText = dv;
17717             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17718             this.value = vv;
17719             return;
17720         }
17721         // no hidden field.. - we store the value in 'value', but still display
17722         // display field!!!!
17723         this.lastSelectionText = dv;
17724         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17725         this.value = vv;
17726         
17727         
17728         
17729     },
17730     // private
17731     reset : function(){
17732         // overridden so that last data is reset..
17733         
17734         if(this.multiple){
17735             this.clearItem();
17736             return;
17737         }
17738         
17739         this.setValue(this.originalValue);
17740         //this.clearInvalid();
17741         this.lastData = false;
17742         if (this.view) {
17743             this.view.clearSelections();
17744         }
17745         
17746         this.validate();
17747     },
17748     // private
17749     findRecord : function(prop, value){
17750         var record;
17751         if(this.store.getCount() > 0){
17752             this.store.each(function(r){
17753                 if(r.data[prop] == value){
17754                     record = r;
17755                     return false;
17756                 }
17757                 return true;
17758             });
17759         }
17760         return record;
17761     },
17762     
17763     getName: function()
17764     {
17765         // returns hidden if it's set..
17766         if (!this.rendered) {return ''};
17767         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17768         
17769     },
17770     // private
17771     onViewMove : function(e, t){
17772         this.inKeyMode = false;
17773     },
17774
17775     // private
17776     onViewOver : function(e, t){
17777         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17778             return;
17779         }
17780         var item = this.view.findItemFromChild(t);
17781         
17782         if(item){
17783             var index = this.view.indexOf(item);
17784             this.select(index, false);
17785         }
17786     },
17787
17788     // private
17789     onViewClick : function(view, doFocus, el, e)
17790     {
17791         var index = this.view.getSelectedIndexes()[0];
17792         
17793         var r = this.store.getAt(index);
17794         
17795         if(this.tickable){
17796             
17797             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17798                 return;
17799             }
17800             
17801             var rm = false;
17802             var _this = this;
17803             
17804             Roo.each(this.tickItems, function(v,k){
17805                 
17806                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17807                     Roo.log(v);
17808                     _this.tickItems.splice(k, 1);
17809                     
17810                     if(typeof(e) == 'undefined' && view == false){
17811                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17812                     }
17813                     
17814                     rm = true;
17815                     return;
17816                 }
17817             });
17818             
17819             if(rm){
17820                 return;
17821             }
17822             
17823             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17824                 this.tickItems.push(r.data);
17825             }
17826             
17827             if(typeof(e) == 'undefined' && view == false){
17828                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17829             }
17830                     
17831             return;
17832         }
17833         
17834         if(r){
17835             this.onSelect(r, index);
17836         }
17837         if(doFocus !== false && !this.blockFocus){
17838             this.inputEl().focus();
17839         }
17840     },
17841
17842     // private
17843     restrictHeight : function(){
17844         //this.innerList.dom.style.height = '';
17845         //var inner = this.innerList.dom;
17846         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17847         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17848         //this.list.beginUpdate();
17849         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17850         this.list.alignTo(this.inputEl(), this.listAlign);
17851         this.list.alignTo(this.inputEl(), this.listAlign);
17852         //this.list.endUpdate();
17853     },
17854
17855     // private
17856     onEmptyResults : function(){
17857         
17858         if(this.tickable && this.editable){
17859             this.hasFocus = false;
17860             this.restrictHeight();
17861             return;
17862         }
17863         
17864         this.collapse();
17865     },
17866
17867     /**
17868      * Returns true if the dropdown list is expanded, else false.
17869      */
17870     isExpanded : function(){
17871         return this.list.isVisible();
17872     },
17873
17874     /**
17875      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17876      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17877      * @param {String} value The data value of the item to select
17878      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17879      * selected item if it is not currently in view (defaults to true)
17880      * @return {Boolean} True if the value matched an item in the list, else false
17881      */
17882     selectByValue : function(v, scrollIntoView){
17883         if(v !== undefined && v !== null){
17884             var r = this.findRecord(this.valueField || this.displayField, v);
17885             if(r){
17886                 this.select(this.store.indexOf(r), scrollIntoView);
17887                 return true;
17888             }
17889         }
17890         return false;
17891     },
17892
17893     /**
17894      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17895      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17896      * @param {Number} index The zero-based index of the list item to select
17897      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17898      * selected item if it is not currently in view (defaults to true)
17899      */
17900     select : function(index, scrollIntoView){
17901         this.selectedIndex = index;
17902         this.view.select(index);
17903         if(scrollIntoView !== false){
17904             var el = this.view.getNode(index);
17905             /*
17906              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17907              */
17908             if(el){
17909                 this.list.scrollChildIntoView(el, false);
17910             }
17911         }
17912     },
17913
17914     // private
17915     selectNext : function(){
17916         var ct = this.store.getCount();
17917         if(ct > 0){
17918             if(this.selectedIndex == -1){
17919                 this.select(0);
17920             }else if(this.selectedIndex < ct-1){
17921                 this.select(this.selectedIndex+1);
17922             }
17923         }
17924     },
17925
17926     // private
17927     selectPrev : function(){
17928         var ct = this.store.getCount();
17929         if(ct > 0){
17930             if(this.selectedIndex == -1){
17931                 this.select(0);
17932             }else if(this.selectedIndex != 0){
17933                 this.select(this.selectedIndex-1);
17934             }
17935         }
17936     },
17937
17938     // private
17939     onKeyUp : function(e){
17940         if(this.editable !== false && !e.isSpecialKey()){
17941             this.lastKey = e.getKey();
17942             this.dqTask.delay(this.queryDelay);
17943         }
17944     },
17945
17946     // private
17947     validateBlur : function(){
17948         return !this.list || !this.list.isVisible();   
17949     },
17950
17951     // private
17952     initQuery : function(){
17953         
17954         var v = this.getRawValue();
17955         
17956         if(this.tickable && this.editable){
17957             v = this.tickableInputEl().getValue();
17958         }
17959         
17960         this.doQuery(v);
17961     },
17962
17963     // private
17964     doForce : function(){
17965         if(this.inputEl().dom.value.length > 0){
17966             this.inputEl().dom.value =
17967                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17968              
17969         }
17970     },
17971
17972     /**
17973      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17974      * query allowing the query action to be canceled if needed.
17975      * @param {String} query The SQL query to execute
17976      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17977      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17978      * saved in the current store (defaults to false)
17979      */
17980     doQuery : function(q, forceAll){
17981         
17982         if(q === undefined || q === null){
17983             q = '';
17984         }
17985         var qe = {
17986             query: q,
17987             forceAll: forceAll,
17988             combo: this,
17989             cancel:false
17990         };
17991         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17992             return false;
17993         }
17994         q = qe.query;
17995         
17996         forceAll = qe.forceAll;
17997         if(forceAll === true || (q.length >= this.minChars)){
17998             
17999             this.hasQuery = true;
18000             
18001             if(this.lastQuery != q || this.alwaysQuery){
18002                 this.lastQuery = q;
18003                 if(this.mode == 'local'){
18004                     this.selectedIndex = -1;
18005                     if(forceAll){
18006                         this.store.clearFilter();
18007                     }else{
18008                         
18009                         if(this.specialFilter){
18010                             this.fireEvent('specialfilter', this);
18011                             this.onLoad();
18012                             return;
18013                         }
18014                         
18015                         this.store.filter(this.displayField, q);
18016                     }
18017                     
18018                     this.store.fireEvent("datachanged", this.store);
18019                     
18020                     this.onLoad();
18021                     
18022                     
18023                 }else{
18024                     
18025                     this.store.baseParams[this.queryParam] = q;
18026                     
18027                     var options = {params : this.getParams(q)};
18028                     
18029                     if(this.loadNext){
18030                         options.add = true;
18031                         options.params.start = this.page * this.pageSize;
18032                     }
18033                     
18034                     this.store.load(options);
18035                     
18036                     /*
18037                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18038                      *  we should expand the list on onLoad
18039                      *  so command out it
18040                      */
18041 //                    this.expand();
18042                 }
18043             }else{
18044                 this.selectedIndex = -1;
18045                 this.onLoad();   
18046             }
18047         }
18048         
18049         this.loadNext = false;
18050     },
18051     
18052     // private
18053     getParams : function(q){
18054         var p = {};
18055         //p[this.queryParam] = q;
18056         
18057         if(this.pageSize){
18058             p.start = 0;
18059             p.limit = this.pageSize;
18060         }
18061         return p;
18062     },
18063
18064     /**
18065      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18066      */
18067     collapse : function(){
18068         if(!this.isExpanded()){
18069             return;
18070         }
18071         
18072         this.list.hide();
18073         
18074         this.hasFocus = false;
18075         
18076         if(this.tickable){
18077             this.okBtn.hide();
18078             this.cancelBtn.hide();
18079             this.trigger.show();
18080             
18081             if(this.editable){
18082                 this.tickableInputEl().dom.value = '';
18083                 this.tickableInputEl().blur();
18084             }
18085             
18086         }
18087         
18088         Roo.get(document).un('mousedown', this.collapseIf, this);
18089         Roo.get(document).un('mousewheel', this.collapseIf, this);
18090         if (!this.editable) {
18091             Roo.get(document).un('keydown', this.listKeyPress, this);
18092         }
18093         this.fireEvent('collapse', this);
18094         
18095         this.validate();
18096     },
18097
18098     // private
18099     collapseIf : function(e){
18100         var in_combo  = e.within(this.el);
18101         var in_list =  e.within(this.list);
18102         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18103         
18104         if (in_combo || in_list || is_list) {
18105             //e.stopPropagation();
18106             return;
18107         }
18108         
18109         if(this.tickable){
18110             this.onTickableFooterButtonClick(e, false, false);
18111         }
18112
18113         this.collapse();
18114         
18115     },
18116
18117     /**
18118      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18119      */
18120     expand : function(){
18121        
18122         if(this.isExpanded() || !this.hasFocus){
18123             return;
18124         }
18125         
18126         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18127         this.list.setWidth(lw);
18128         
18129         Roo.log('expand');
18130         
18131         this.list.show();
18132         
18133         this.restrictHeight();
18134         
18135         if(this.tickable){
18136             
18137             this.tickItems = Roo.apply([], this.item);
18138             
18139             this.okBtn.show();
18140             this.cancelBtn.show();
18141             this.trigger.hide();
18142             
18143             if(this.editable){
18144                 this.tickableInputEl().focus();
18145             }
18146             
18147         }
18148         
18149         Roo.get(document).on('mousedown', this.collapseIf, this);
18150         Roo.get(document).on('mousewheel', this.collapseIf, this);
18151         if (!this.editable) {
18152             Roo.get(document).on('keydown', this.listKeyPress, this);
18153         }
18154         
18155         this.fireEvent('expand', this);
18156     },
18157
18158     // private
18159     // Implements the default empty TriggerField.onTriggerClick function
18160     onTriggerClick : function(e)
18161     {
18162         Roo.log('trigger click');
18163         
18164         if(this.disabled || !this.triggerList){
18165             return;
18166         }
18167         
18168         this.page = 0;
18169         this.loadNext = false;
18170         
18171         if(this.isExpanded()){
18172             this.collapse();
18173             if (!this.blockFocus) {
18174                 this.inputEl().focus();
18175             }
18176             
18177         }else {
18178             this.hasFocus = true;
18179             if(this.triggerAction == 'all') {
18180                 this.doQuery(this.allQuery, true);
18181             } else {
18182                 this.doQuery(this.getRawValue());
18183             }
18184             if (!this.blockFocus) {
18185                 this.inputEl().focus();
18186             }
18187         }
18188     },
18189     
18190     onTickableTriggerClick : function(e)
18191     {
18192         if(this.disabled){
18193             return;
18194         }
18195         
18196         this.page = 0;
18197         this.loadNext = false;
18198         this.hasFocus = true;
18199         
18200         if(this.triggerAction == 'all') {
18201             this.doQuery(this.allQuery, true);
18202         } else {
18203             this.doQuery(this.getRawValue());
18204         }
18205     },
18206     
18207     onSearchFieldClick : function(e)
18208     {
18209         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18210             this.onTickableFooterButtonClick(e, false, false);
18211             return;
18212         }
18213         
18214         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18215             return;
18216         }
18217         
18218         this.page = 0;
18219         this.loadNext = false;
18220         this.hasFocus = true;
18221         
18222         if(this.triggerAction == 'all') {
18223             this.doQuery(this.allQuery, true);
18224         } else {
18225             this.doQuery(this.getRawValue());
18226         }
18227     },
18228     
18229     listKeyPress : function(e)
18230     {
18231         //Roo.log('listkeypress');
18232         // scroll to first matching element based on key pres..
18233         if (e.isSpecialKey()) {
18234             return false;
18235         }
18236         var k = String.fromCharCode(e.getKey()).toUpperCase();
18237         //Roo.log(k);
18238         var match  = false;
18239         var csel = this.view.getSelectedNodes();
18240         var cselitem = false;
18241         if (csel.length) {
18242             var ix = this.view.indexOf(csel[0]);
18243             cselitem  = this.store.getAt(ix);
18244             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18245                 cselitem = false;
18246             }
18247             
18248         }
18249         
18250         this.store.each(function(v) { 
18251             if (cselitem) {
18252                 // start at existing selection.
18253                 if (cselitem.id == v.id) {
18254                     cselitem = false;
18255                 }
18256                 return true;
18257             }
18258                 
18259             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18260                 match = this.store.indexOf(v);
18261                 return false;
18262             }
18263             return true;
18264         }, this);
18265         
18266         if (match === false) {
18267             return true; // no more action?
18268         }
18269         // scroll to?
18270         this.view.select(match);
18271         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18272         sn.scrollIntoView(sn.dom.parentNode, false);
18273     },
18274     
18275     onViewScroll : function(e, t){
18276         
18277         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){
18278             return;
18279         }
18280         
18281         this.hasQuery = true;
18282         
18283         this.loading = this.list.select('.loading', true).first();
18284         
18285         if(this.loading === null){
18286             this.list.createChild({
18287                 tag: 'div',
18288                 cls: 'loading roo-select2-more-results roo-select2-active',
18289                 html: 'Loading more results...'
18290             });
18291             
18292             this.loading = this.list.select('.loading', true).first();
18293             
18294             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18295             
18296             this.loading.hide();
18297         }
18298         
18299         this.loading.show();
18300         
18301         var _combo = this;
18302         
18303         this.page++;
18304         this.loadNext = true;
18305         
18306         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18307         
18308         return;
18309     },
18310     
18311     addItem : function(o)
18312     {   
18313         var dv = ''; // display value
18314         
18315         if (this.displayField) {
18316             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18317         } else {
18318             // this is an error condition!!!
18319             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18320         }
18321         
18322         if(!dv.length){
18323             return;
18324         }
18325         
18326         var choice = this.choices.createChild({
18327             tag: 'li',
18328             cls: 'roo-select2-search-choice',
18329             cn: [
18330                 {
18331                     tag: 'div',
18332                     html: dv
18333                 },
18334                 {
18335                     tag: 'a',
18336                     href: '#',
18337                     cls: 'roo-select2-search-choice-close fa fa-times',
18338                     tabindex: '-1'
18339                 }
18340             ]
18341             
18342         }, this.searchField);
18343         
18344         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18345         
18346         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18347         
18348         this.item.push(o);
18349         
18350         this.lastData = o;
18351         
18352         this.syncValue();
18353         
18354         this.inputEl().dom.value = '';
18355         
18356         this.validate();
18357     },
18358     
18359     onRemoveItem : function(e, _self, o)
18360     {
18361         e.preventDefault();
18362         
18363         this.lastItem = Roo.apply([], this.item);
18364         
18365         var index = this.item.indexOf(o.data) * 1;
18366         
18367         if( index < 0){
18368             Roo.log('not this item?!');
18369             return;
18370         }
18371         
18372         this.item.splice(index, 1);
18373         o.item.remove();
18374         
18375         this.syncValue();
18376         
18377         this.fireEvent('remove', this, e);
18378         
18379         this.validate();
18380         
18381     },
18382     
18383     syncValue : function()
18384     {
18385         if(!this.item.length){
18386             this.clearValue();
18387             return;
18388         }
18389             
18390         var value = [];
18391         var _this = this;
18392         Roo.each(this.item, function(i){
18393             if(_this.valueField){
18394                 value.push(i[_this.valueField]);
18395                 return;
18396             }
18397
18398             value.push(i);
18399         });
18400
18401         this.value = value.join(',');
18402
18403         if(this.hiddenField){
18404             this.hiddenField.dom.value = this.value;
18405         }
18406         
18407         this.store.fireEvent("datachanged", this.store);
18408         
18409         this.validate();
18410     },
18411     
18412     clearItem : function()
18413     {
18414         if(!this.multiple){
18415             return;
18416         }
18417         
18418         this.item = [];
18419         
18420         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18421            c.remove();
18422         });
18423         
18424         this.syncValue();
18425         
18426         this.validate();
18427         
18428         if(this.tickable && !Roo.isTouch){
18429             this.view.refresh();
18430         }
18431     },
18432     
18433     inputEl: function ()
18434     {
18435         if(Roo.isIOS && this.useNativeIOS){
18436             return this.el.select('select.roo-ios-select', true).first();
18437         }
18438         
18439         if(Roo.isTouch && this.mobileTouchView){
18440             return this.el.select('input.form-control',true).first();
18441         }
18442         
18443         if(this.tickable){
18444             return this.searchField;
18445         }
18446         
18447         return this.el.select('input.form-control',true).first();
18448     },
18449     
18450     onTickableFooterButtonClick : function(e, btn, el)
18451     {
18452         e.preventDefault();
18453         
18454         this.lastItem = Roo.apply([], this.item);
18455         
18456         if(btn && btn.name == 'cancel'){
18457             this.tickItems = Roo.apply([], this.item);
18458             this.collapse();
18459             return;
18460         }
18461         
18462         this.clearItem();
18463         
18464         var _this = this;
18465         
18466         Roo.each(this.tickItems, function(o){
18467             _this.addItem(o);
18468         });
18469         
18470         this.collapse();
18471         
18472     },
18473     
18474     validate : function()
18475     {
18476         if(this.getVisibilityEl().hasClass('hidden')){
18477             return true;
18478         }
18479         
18480         var v = this.getRawValue();
18481         
18482         if(this.multiple){
18483             v = this.getValue();
18484         }
18485         
18486         if(this.disabled || this.allowBlank || v.length){
18487             this.markValid();
18488             return true;
18489         }
18490         
18491         this.markInvalid();
18492         return false;
18493     },
18494     
18495     tickableInputEl : function()
18496     {
18497         if(!this.tickable || !this.editable){
18498             return this.inputEl();
18499         }
18500         
18501         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18502     },
18503     
18504     
18505     getAutoCreateTouchView : function()
18506     {
18507         var id = Roo.id();
18508         
18509         var cfg = {
18510             cls: 'form-group' //input-group
18511         };
18512         
18513         var input =  {
18514             tag: 'input',
18515             id : id,
18516             type : this.inputType,
18517             cls : 'form-control x-combo-noedit',
18518             autocomplete: 'new-password',
18519             placeholder : this.placeholder || '',
18520             readonly : true
18521         };
18522         
18523         if (this.name) {
18524             input.name = this.name;
18525         }
18526         
18527         if (this.size) {
18528             input.cls += ' input-' + this.size;
18529         }
18530         
18531         if (this.disabled) {
18532             input.disabled = true;
18533         }
18534         
18535         var inputblock = {
18536             cls : 'roo-combobox-wrap',
18537             cn : [
18538                 input
18539             ]
18540         };
18541         
18542         if(this.before){
18543             inputblock.cls += ' input-group';
18544             
18545             inputblock.cn.unshift({
18546                 tag :'span',
18547                 cls : 'input-group-addon input-group-prepend input-group-text',
18548                 html : this.before
18549             });
18550         }
18551         
18552         if(this.removable && !this.multiple){
18553             inputblock.cls += ' roo-removable';
18554             
18555             inputblock.cn.push({
18556                 tag: 'button',
18557                 html : 'x',
18558                 cls : 'roo-combo-removable-btn close'
18559             });
18560         }
18561
18562         if(this.hasFeedback && !this.allowBlank){
18563             
18564             inputblock.cls += ' has-feedback';
18565             
18566             inputblock.cn.push({
18567                 tag: 'span',
18568                 cls: 'glyphicon form-control-feedback'
18569             });
18570             
18571         }
18572         
18573         if (this.after) {
18574             
18575             inputblock.cls += (this.before) ? '' : ' input-group';
18576             
18577             inputblock.cn.push({
18578                 tag :'span',
18579                 cls : 'input-group-addon input-group-append input-group-text',
18580                 html : this.after
18581             });
18582         }
18583
18584         
18585         var ibwrap = inputblock;
18586         
18587         if(this.multiple){
18588             ibwrap = {
18589                 tag: 'ul',
18590                 cls: 'roo-select2-choices',
18591                 cn:[
18592                     {
18593                         tag: 'li',
18594                         cls: 'roo-select2-search-field',
18595                         cn: [
18596
18597                             inputblock
18598                         ]
18599                     }
18600                 ]
18601             };
18602         
18603             
18604         }
18605         
18606         var combobox = {
18607             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18608             cn: [
18609                 {
18610                     tag: 'input',
18611                     type : 'hidden',
18612                     cls: 'form-hidden-field'
18613                 },
18614                 ibwrap
18615             ]
18616         };
18617         
18618         if(!this.multiple && this.showToggleBtn){
18619             
18620             var caret = {
18621                 cls: 'caret'
18622             };
18623             
18624             if (this.caret != false) {
18625                 caret = {
18626                      tag: 'i',
18627                      cls: 'fa fa-' + this.caret
18628                 };
18629                 
18630             }
18631             
18632             combobox.cn.push({
18633                 tag :'span',
18634                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18635                 cn : [
18636                     Roo.bootstrap.version == 3 ? caret : '',
18637                     {
18638                         tag: 'span',
18639                         cls: 'combobox-clear',
18640                         cn  : [
18641                             {
18642                                 tag : 'i',
18643                                 cls: 'icon-remove'
18644                             }
18645                         ]
18646                     }
18647                 ]
18648
18649             })
18650         }
18651         
18652         if(this.multiple){
18653             combobox.cls += ' roo-select2-container-multi';
18654         }
18655         
18656         var required =  this.allowBlank ?  {
18657                     tag : 'i',
18658                     style: 'display: none'
18659                 } : {
18660                    tag : 'i',
18661                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18662                    tooltip : 'This field is required'
18663                 };
18664         
18665         var align = this.labelAlign || this.parentLabelAlign();
18666         
18667         if (align ==='left' && this.fieldLabel.length) {
18668
18669             cfg.cn = [
18670                 required,
18671                 {
18672                     tag: 'label',
18673                     cls : 'control-label col-form-label',
18674                     html : this.fieldLabel
18675
18676                 },
18677                 {
18678                     cls : 'roo-combobox-wrap ', 
18679                     cn: [
18680                         combobox
18681                     ]
18682                 }
18683             ];
18684             
18685             var labelCfg = cfg.cn[1];
18686             var contentCfg = cfg.cn[2];
18687             
18688
18689             if(this.indicatorpos == 'right'){
18690                 cfg.cn = [
18691                     {
18692                         tag: 'label',
18693                         'for' :  id,
18694                         cls : 'control-label col-form-label',
18695                         cn : [
18696                             {
18697                                 tag : 'span',
18698                                 html : this.fieldLabel
18699                             },
18700                             required
18701                         ]
18702                     },
18703                     {
18704                         cls : "roo-combobox-wrap ",
18705                         cn: [
18706                             combobox
18707                         ]
18708                     }
18709
18710                 ];
18711                 
18712                 labelCfg = cfg.cn[0];
18713                 contentCfg = cfg.cn[1];
18714             }
18715             
18716            
18717             
18718             if(this.labelWidth > 12){
18719                 labelCfg.style = "width: " + this.labelWidth + 'px';
18720             }
18721            
18722             if(this.labelWidth < 13 && this.labelmd == 0){
18723                 this.labelmd = this.labelWidth;
18724             }
18725             
18726             if(this.labellg > 0){
18727                 labelCfg.cls += ' col-lg-' + this.labellg;
18728                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18729             }
18730             
18731             if(this.labelmd > 0){
18732                 labelCfg.cls += ' col-md-' + this.labelmd;
18733                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18734             }
18735             
18736             if(this.labelsm > 0){
18737                 labelCfg.cls += ' col-sm-' + this.labelsm;
18738                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18739             }
18740             
18741             if(this.labelxs > 0){
18742                 labelCfg.cls += ' col-xs-' + this.labelxs;
18743                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18744             }
18745                 
18746                 
18747         } else if ( this.fieldLabel.length) {
18748             cfg.cn = [
18749                required,
18750                 {
18751                     tag: 'label',
18752                     cls : 'control-label',
18753                     html : this.fieldLabel
18754
18755                 },
18756                 {
18757                     cls : '', 
18758                     cn: [
18759                         combobox
18760                     ]
18761                 }
18762             ];
18763             
18764             if(this.indicatorpos == 'right'){
18765                 cfg.cn = [
18766                     {
18767                         tag: 'label',
18768                         cls : 'control-label',
18769                         html : this.fieldLabel,
18770                         cn : [
18771                             required
18772                         ]
18773                     },
18774                     {
18775                         cls : '', 
18776                         cn: [
18777                             combobox
18778                         ]
18779                     }
18780                 ];
18781             }
18782         } else {
18783             cfg.cn = combobox;    
18784         }
18785         
18786         
18787         var settings = this;
18788         
18789         ['xs','sm','md','lg'].map(function(size){
18790             if (settings[size]) {
18791                 cfg.cls += ' col-' + size + '-' + settings[size];
18792             }
18793         });
18794         
18795         return cfg;
18796     },
18797     
18798     initTouchView : function()
18799     {
18800         this.renderTouchView();
18801         
18802         this.touchViewEl.on('scroll', function(){
18803             this.el.dom.scrollTop = 0;
18804         }, this);
18805         
18806         this.originalValue = this.getValue();
18807         
18808         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18809         
18810         this.inputEl().on("click", this.showTouchView, this);
18811         if (this.triggerEl) {
18812             this.triggerEl.on("click", this.showTouchView, this);
18813         }
18814         
18815         
18816         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18817         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18818         
18819         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18820         
18821         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18822         this.store.on('load', this.onTouchViewLoad, this);
18823         this.store.on('loadexception', this.onTouchViewLoadException, this);
18824         
18825         if(this.hiddenName){
18826             
18827             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18828             
18829             this.hiddenField.dom.value =
18830                 this.hiddenValue !== undefined ? this.hiddenValue :
18831                 this.value !== undefined ? this.value : '';
18832         
18833             this.el.dom.removeAttribute('name');
18834             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18835         }
18836         
18837         if(this.multiple){
18838             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18839             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18840         }
18841         
18842         if(this.removable && !this.multiple){
18843             var close = this.closeTriggerEl();
18844             if(close){
18845                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18846                 close.on('click', this.removeBtnClick, this, close);
18847             }
18848         }
18849         /*
18850          * fix the bug in Safari iOS8
18851          */
18852         this.inputEl().on("focus", function(e){
18853             document.activeElement.blur();
18854         }, this);
18855         
18856         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18857         
18858         return;
18859         
18860         
18861     },
18862     
18863     renderTouchView : function()
18864     {
18865         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18866         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18867         
18868         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18869         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18870         
18871         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18872         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18873         this.touchViewBodyEl.setStyle('overflow', 'auto');
18874         
18875         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18876         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18877         
18878         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18879         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880         
18881     },
18882     
18883     showTouchView : function()
18884     {
18885         if(this.disabled){
18886             return;
18887         }
18888         
18889         this.touchViewHeaderEl.hide();
18890
18891         if(this.modalTitle.length){
18892             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18893             this.touchViewHeaderEl.show();
18894         }
18895
18896         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18897         this.touchViewEl.show();
18898
18899         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18900         
18901         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18902         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18903
18904         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18905
18906         if(this.modalTitle.length){
18907             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18908         }
18909         
18910         this.touchViewBodyEl.setHeight(bodyHeight);
18911
18912         if(this.animate){
18913             var _this = this;
18914             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18915         }else{
18916             this.touchViewEl.addClass(['in','show']);
18917         }
18918         
18919         if(this._touchViewMask){
18920             Roo.get(document.body).addClass("x-body-masked");
18921             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18922             this._touchViewMask.setStyle('z-index', 10000);
18923             this._touchViewMask.addClass('show');
18924         }
18925         
18926         this.doTouchViewQuery();
18927         
18928     },
18929     
18930     hideTouchView : function()
18931     {
18932         this.touchViewEl.removeClass(['in','show']);
18933
18934         if(this.animate){
18935             var _this = this;
18936             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18937         }else{
18938             this.touchViewEl.setStyle('display', 'none');
18939         }
18940         
18941         if(this._touchViewMask){
18942             this._touchViewMask.removeClass('show');
18943             Roo.get(document.body).removeClass("x-body-masked");
18944         }
18945     },
18946     
18947     setTouchViewValue : function()
18948     {
18949         if(this.multiple){
18950             this.clearItem();
18951         
18952             var _this = this;
18953
18954             Roo.each(this.tickItems, function(o){
18955                 this.addItem(o);
18956             }, this);
18957         }
18958         
18959         this.hideTouchView();
18960     },
18961     
18962     doTouchViewQuery : function()
18963     {
18964         var qe = {
18965             query: '',
18966             forceAll: true,
18967             combo: this,
18968             cancel:false
18969         };
18970         
18971         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18972             return false;
18973         }
18974         
18975         if(!this.alwaysQuery || this.mode == 'local'){
18976             this.onTouchViewLoad();
18977             return;
18978         }
18979         
18980         this.store.load();
18981     },
18982     
18983     onTouchViewBeforeLoad : function(combo,opts)
18984     {
18985         return;
18986     },
18987
18988     // private
18989     onTouchViewLoad : function()
18990     {
18991         if(this.store.getCount() < 1){
18992             this.onTouchViewEmptyResults();
18993             return;
18994         }
18995         
18996         this.clearTouchView();
18997         
18998         var rawValue = this.getRawValue();
18999         
19000         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19001         
19002         this.tickItems = [];
19003         
19004         this.store.data.each(function(d, rowIndex){
19005             var row = this.touchViewListGroup.createChild(template);
19006             
19007             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19008                 row.addClass(d.data.cls);
19009             }
19010             
19011             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19012                 var cfg = {
19013                     data : d.data,
19014                     html : d.data[this.displayField]
19015                 };
19016                 
19017                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19018                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19019                 }
19020             }
19021             row.removeClass('selected');
19022             if(!this.multiple && this.valueField &&
19023                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19024             {
19025                 // radio buttons..
19026                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19027                 row.addClass('selected');
19028             }
19029             
19030             if(this.multiple && this.valueField &&
19031                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19032             {
19033                 
19034                 // checkboxes...
19035                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19036                 this.tickItems.push(d.data);
19037             }
19038             
19039             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19040             
19041         }, this);
19042         
19043         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19044         
19045         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19046
19047         if(this.modalTitle.length){
19048             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19049         }
19050
19051         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19052         
19053         if(this.mobile_restrict_height && listHeight < bodyHeight){
19054             this.touchViewBodyEl.setHeight(listHeight);
19055         }
19056         
19057         var _this = this;
19058         
19059         if(firstChecked && listHeight > bodyHeight){
19060             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19061         }
19062         
19063     },
19064     
19065     onTouchViewLoadException : function()
19066     {
19067         this.hideTouchView();
19068     },
19069     
19070     onTouchViewEmptyResults : function()
19071     {
19072         this.clearTouchView();
19073         
19074         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19075         
19076         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19077         
19078     },
19079     
19080     clearTouchView : function()
19081     {
19082         this.touchViewListGroup.dom.innerHTML = '';
19083     },
19084     
19085     onTouchViewClick : function(e, el, o)
19086     {
19087         e.preventDefault();
19088         
19089         var row = o.row;
19090         var rowIndex = o.rowIndex;
19091         
19092         var r = this.store.getAt(rowIndex);
19093         
19094         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19095             
19096             if(!this.multiple){
19097                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19098                     c.dom.removeAttribute('checked');
19099                 }, this);
19100
19101                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19102
19103                 this.setFromData(r.data);
19104
19105                 var close = this.closeTriggerEl();
19106
19107                 if(close){
19108                     close.show();
19109                 }
19110
19111                 this.hideTouchView();
19112
19113                 this.fireEvent('select', this, r, rowIndex);
19114
19115                 return;
19116             }
19117
19118             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19119                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19120                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19121                 return;
19122             }
19123
19124             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19125             this.addItem(r.data);
19126             this.tickItems.push(r.data);
19127         }
19128     },
19129     
19130     getAutoCreateNativeIOS : function()
19131     {
19132         var cfg = {
19133             cls: 'form-group' //input-group,
19134         };
19135         
19136         var combobox =  {
19137             tag: 'select',
19138             cls : 'roo-ios-select'
19139         };
19140         
19141         if (this.name) {
19142             combobox.name = this.name;
19143         }
19144         
19145         if (this.disabled) {
19146             combobox.disabled = true;
19147         }
19148         
19149         var settings = this;
19150         
19151         ['xs','sm','md','lg'].map(function(size){
19152             if (settings[size]) {
19153                 cfg.cls += ' col-' + size + '-' + settings[size];
19154             }
19155         });
19156         
19157         cfg.cn = combobox;
19158         
19159         return cfg;
19160         
19161     },
19162     
19163     initIOSView : function()
19164     {
19165         this.store.on('load', this.onIOSViewLoad, this);
19166         
19167         return;
19168     },
19169     
19170     onIOSViewLoad : function()
19171     {
19172         if(this.store.getCount() < 1){
19173             return;
19174         }
19175         
19176         this.clearIOSView();
19177         
19178         if(this.allowBlank) {
19179             
19180             var default_text = '-- SELECT --';
19181             
19182             if(this.placeholder.length){
19183                 default_text = this.placeholder;
19184             }
19185             
19186             if(this.emptyTitle.length){
19187                 default_text += ' - ' + this.emptyTitle + ' -';
19188             }
19189             
19190             var opt = this.inputEl().createChild({
19191                 tag: 'option',
19192                 value : 0,
19193                 html : default_text
19194             });
19195             
19196             var o = {};
19197             o[this.valueField] = 0;
19198             o[this.displayField] = default_text;
19199             
19200             this.ios_options.push({
19201                 data : o,
19202                 el : opt
19203             });
19204             
19205         }
19206         
19207         this.store.data.each(function(d, rowIndex){
19208             
19209             var html = '';
19210             
19211             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19212                 html = d.data[this.displayField];
19213             }
19214             
19215             var value = '';
19216             
19217             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19218                 value = d.data[this.valueField];
19219             }
19220             
19221             var option = {
19222                 tag: 'option',
19223                 value : value,
19224                 html : html
19225             };
19226             
19227             if(this.value == d.data[this.valueField]){
19228                 option['selected'] = true;
19229             }
19230             
19231             var opt = this.inputEl().createChild(option);
19232             
19233             this.ios_options.push({
19234                 data : d.data,
19235                 el : opt
19236             });
19237             
19238         }, this);
19239         
19240         this.inputEl().on('change', function(){
19241            this.fireEvent('select', this);
19242         }, this);
19243         
19244     },
19245     
19246     clearIOSView: function()
19247     {
19248         this.inputEl().dom.innerHTML = '';
19249         
19250         this.ios_options = [];
19251     },
19252     
19253     setIOSValue: function(v)
19254     {
19255         this.value = v;
19256         
19257         if(!this.ios_options){
19258             return;
19259         }
19260         
19261         Roo.each(this.ios_options, function(opts){
19262            
19263            opts.el.dom.removeAttribute('selected');
19264            
19265            if(opts.data[this.valueField] != v){
19266                return;
19267            }
19268            
19269            opts.el.dom.setAttribute('selected', true);
19270            
19271         }, this);
19272     }
19273
19274     /** 
19275     * @cfg {Boolean} grow 
19276     * @hide 
19277     */
19278     /** 
19279     * @cfg {Number} growMin 
19280     * @hide 
19281     */
19282     /** 
19283     * @cfg {Number} growMax 
19284     * @hide 
19285     */
19286     /**
19287      * @hide
19288      * @method autoSize
19289      */
19290 });
19291
19292 Roo.apply(Roo.bootstrap.ComboBox,  {
19293     
19294     header : {
19295         tag: 'div',
19296         cls: 'modal-header',
19297         cn: [
19298             {
19299                 tag: 'h4',
19300                 cls: 'modal-title'
19301             }
19302         ]
19303     },
19304     
19305     body : {
19306         tag: 'div',
19307         cls: 'modal-body',
19308         cn: [
19309             {
19310                 tag: 'ul',
19311                 cls: 'list-group'
19312             }
19313         ]
19314     },
19315     
19316     listItemRadio : {
19317         tag: 'li',
19318         cls: 'list-group-item',
19319         cn: [
19320             {
19321                 tag: 'span',
19322                 cls: 'roo-combobox-list-group-item-value'
19323             },
19324             {
19325                 tag: 'div',
19326                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19327                 cn: [
19328                     {
19329                         tag: 'input',
19330                         type: 'radio'
19331                     },
19332                     {
19333                         tag: 'label'
19334                     }
19335                 ]
19336             }
19337         ]
19338     },
19339     
19340     listItemCheckbox : {
19341         tag: 'li',
19342         cls: 'list-group-item',
19343         cn: [
19344             {
19345                 tag: 'span',
19346                 cls: 'roo-combobox-list-group-item-value'
19347             },
19348             {
19349                 tag: 'div',
19350                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19351                 cn: [
19352                     {
19353                         tag: 'input',
19354                         type: 'checkbox'
19355                     },
19356                     {
19357                         tag: 'label'
19358                     }
19359                 ]
19360             }
19361         ]
19362     },
19363     
19364     emptyResult : {
19365         tag: 'div',
19366         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19367     },
19368     
19369     footer : {
19370         tag: 'div',
19371         cls: 'modal-footer',
19372         cn: [
19373             {
19374                 tag: 'div',
19375                 cls: 'row',
19376                 cn: [
19377                     {
19378                         tag: 'div',
19379                         cls: 'col-xs-6 text-left',
19380                         cn: {
19381                             tag: 'button',
19382                             cls: 'btn btn-danger roo-touch-view-cancel',
19383                             html: 'Cancel'
19384                         }
19385                     },
19386                     {
19387                         tag: 'div',
19388                         cls: 'col-xs-6 text-right',
19389                         cn: {
19390                             tag: 'button',
19391                             cls: 'btn btn-success roo-touch-view-ok',
19392                             html: 'OK'
19393                         }
19394                     }
19395                 ]
19396             }
19397         ]
19398         
19399     }
19400 });
19401
19402 Roo.apply(Roo.bootstrap.ComboBox,  {
19403     
19404     touchViewTemplate : {
19405         tag: 'div',
19406         cls: 'modal fade roo-combobox-touch-view',
19407         cn: [
19408             {
19409                 tag: 'div',
19410                 cls: 'modal-dialog',
19411                 style : 'position:fixed', // we have to fix position....
19412                 cn: [
19413                     {
19414                         tag: 'div',
19415                         cls: 'modal-content',
19416                         cn: [
19417                             Roo.bootstrap.ComboBox.header,
19418                             Roo.bootstrap.ComboBox.body,
19419                             Roo.bootstrap.ComboBox.footer
19420                         ]
19421                     }
19422                 ]
19423             }
19424         ]
19425     }
19426 });/*
19427  * Based on:
19428  * Ext JS Library 1.1.1
19429  * Copyright(c) 2006-2007, Ext JS, LLC.
19430  *
19431  * Originally Released Under LGPL - original licence link has changed is not relivant.
19432  *
19433  * Fork - LGPL
19434  * <script type="text/javascript">
19435  */
19436
19437 /**
19438  * @class Roo.View
19439  * @extends Roo.util.Observable
19440  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19441  * This class also supports single and multi selection modes. <br>
19442  * Create a data model bound view:
19443  <pre><code>
19444  var store = new Roo.data.Store(...);
19445
19446  var view = new Roo.View({
19447     el : "my-element",
19448     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19449  
19450     singleSelect: true,
19451     selectedClass: "ydataview-selected",
19452     store: store
19453  });
19454
19455  // listen for node click?
19456  view.on("click", function(vw, index, node, e){
19457  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19458  });
19459
19460  // load XML data
19461  dataModel.load("foobar.xml");
19462  </code></pre>
19463  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19464  * <br><br>
19465  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19466  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19467  * 
19468  * Note: old style constructor is still suported (container, template, config)
19469  * 
19470  * @constructor
19471  * Create a new View
19472  * @param {Object} config The config object
19473  * 
19474  */
19475 Roo.View = function(config, depreciated_tpl, depreciated_config){
19476     
19477     this.parent = false;
19478     
19479     if (typeof(depreciated_tpl) == 'undefined') {
19480         // new way.. - universal constructor.
19481         Roo.apply(this, config);
19482         this.el  = Roo.get(this.el);
19483     } else {
19484         // old format..
19485         this.el  = Roo.get(config);
19486         this.tpl = depreciated_tpl;
19487         Roo.apply(this, depreciated_config);
19488     }
19489     this.wrapEl  = this.el.wrap().wrap();
19490     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19491     
19492     
19493     if(typeof(this.tpl) == "string"){
19494         this.tpl = new Roo.Template(this.tpl);
19495     } else {
19496         // support xtype ctors..
19497         this.tpl = new Roo.factory(this.tpl, Roo);
19498     }
19499     
19500     
19501     this.tpl.compile();
19502     
19503     /** @private */
19504     this.addEvents({
19505         /**
19506          * @event beforeclick
19507          * Fires before a click is processed. Returns false to cancel the default action.
19508          * @param {Roo.View} this
19509          * @param {Number} index The index of the target node
19510          * @param {HTMLElement} node The target node
19511          * @param {Roo.EventObject} e The raw event object
19512          */
19513             "beforeclick" : true,
19514         /**
19515          * @event click
19516          * Fires when a template node is clicked.
19517          * @param {Roo.View} this
19518          * @param {Number} index The index of the target node
19519          * @param {HTMLElement} node The target node
19520          * @param {Roo.EventObject} e The raw event object
19521          */
19522             "click" : true,
19523         /**
19524          * @event dblclick
19525          * Fires when a template node is double clicked.
19526          * @param {Roo.View} this
19527          * @param {Number} index The index of the target node
19528          * @param {HTMLElement} node The target node
19529          * @param {Roo.EventObject} e The raw event object
19530          */
19531             "dblclick" : true,
19532         /**
19533          * @event contextmenu
19534          * Fires when a template node is right clicked.
19535          * @param {Roo.View} this
19536          * @param {Number} index The index of the target node
19537          * @param {HTMLElement} node The target node
19538          * @param {Roo.EventObject} e The raw event object
19539          */
19540             "contextmenu" : true,
19541         /**
19542          * @event selectionchange
19543          * Fires when the selected nodes change.
19544          * @param {Roo.View} this
19545          * @param {Array} selections Array of the selected nodes
19546          */
19547             "selectionchange" : true,
19548     
19549         /**
19550          * @event beforeselect
19551          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19552          * @param {Roo.View} this
19553          * @param {HTMLElement} node The node to be selected
19554          * @param {Array} selections Array of currently selected nodes
19555          */
19556             "beforeselect" : true,
19557         /**
19558          * @event preparedata
19559          * Fires on every row to render, to allow you to change the data.
19560          * @param {Roo.View} this
19561          * @param {Object} data to be rendered (change this)
19562          */
19563           "preparedata" : true
19564           
19565           
19566         });
19567
19568
19569
19570     this.el.on({
19571         "click": this.onClick,
19572         "dblclick": this.onDblClick,
19573         "contextmenu": this.onContextMenu,
19574         scope:this
19575     });
19576
19577     this.selections = [];
19578     this.nodes = [];
19579     this.cmp = new Roo.CompositeElementLite([]);
19580     if(this.store){
19581         this.store = Roo.factory(this.store, Roo.data);
19582         this.setStore(this.store, true);
19583     }
19584     
19585     if ( this.footer && this.footer.xtype) {
19586            
19587          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19588         
19589         this.footer.dataSource = this.store;
19590         this.footer.container = fctr;
19591         this.footer = Roo.factory(this.footer, Roo);
19592         fctr.insertFirst(this.el);
19593         
19594         // this is a bit insane - as the paging toolbar seems to detach the el..
19595 //        dom.parentNode.parentNode.parentNode
19596          // they get detached?
19597     }
19598     
19599     
19600     Roo.View.superclass.constructor.call(this);
19601     
19602     
19603 };
19604
19605 Roo.extend(Roo.View, Roo.util.Observable, {
19606     
19607      /**
19608      * @cfg {Roo.data.Store} store Data store to load data from.
19609      */
19610     store : false,
19611     
19612     /**
19613      * @cfg {String|Roo.Element} el The container element.
19614      */
19615     el : '',
19616     
19617     /**
19618      * @cfg {String|Roo.Template} tpl The template used by this View 
19619      */
19620     tpl : false,
19621     /**
19622      * @cfg {String} dataName the named area of the template to use as the data area
19623      *                          Works with domtemplates roo-name="name"
19624      */
19625     dataName: false,
19626     /**
19627      * @cfg {String} selectedClass The css class to add to selected nodes
19628      */
19629     selectedClass : "x-view-selected",
19630      /**
19631      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19632      */
19633     emptyText : "",
19634     
19635     /**
19636      * @cfg {String} text to display on mask (default Loading)
19637      */
19638     mask : false,
19639     /**
19640      * @cfg {Boolean} multiSelect Allow multiple selection
19641      */
19642     multiSelect : false,
19643     /**
19644      * @cfg {Boolean} singleSelect Allow single selection
19645      */
19646     singleSelect:  false,
19647     
19648     /**
19649      * @cfg {Boolean} toggleSelect - selecting 
19650      */
19651     toggleSelect : false,
19652     
19653     /**
19654      * @cfg {Boolean} tickable - selecting 
19655      */
19656     tickable : false,
19657     
19658     /**
19659      * Returns the element this view is bound to.
19660      * @return {Roo.Element}
19661      */
19662     getEl : function(){
19663         return this.wrapEl;
19664     },
19665     
19666     
19667
19668     /**
19669      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19670      */
19671     refresh : function(){
19672         //Roo.log('refresh');
19673         var t = this.tpl;
19674         
19675         // if we are using something like 'domtemplate', then
19676         // the what gets used is:
19677         // t.applySubtemplate(NAME, data, wrapping data..)
19678         // the outer template then get' applied with
19679         //     the store 'extra data'
19680         // and the body get's added to the
19681         //      roo-name="data" node?
19682         //      <span class='roo-tpl-{name}'></span> ?????
19683         
19684         
19685         
19686         this.clearSelections();
19687         this.el.update("");
19688         var html = [];
19689         var records = this.store.getRange();
19690         if(records.length < 1) {
19691             
19692             // is this valid??  = should it render a template??
19693             
19694             this.el.update(this.emptyText);
19695             return;
19696         }
19697         var el = this.el;
19698         if (this.dataName) {
19699             this.el.update(t.apply(this.store.meta)); //????
19700             el = this.el.child('.roo-tpl-' + this.dataName);
19701         }
19702         
19703         for(var i = 0, len = records.length; i < len; i++){
19704             var data = this.prepareData(records[i].data, i, records[i]);
19705             this.fireEvent("preparedata", this, data, i, records[i]);
19706             
19707             var d = Roo.apply({}, data);
19708             
19709             if(this.tickable){
19710                 Roo.apply(d, {'roo-id' : Roo.id()});
19711                 
19712                 var _this = this;
19713             
19714                 Roo.each(this.parent.item, function(item){
19715                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19716                         return;
19717                     }
19718                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19719                 });
19720             }
19721             
19722             html[html.length] = Roo.util.Format.trim(
19723                 this.dataName ?
19724                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19725                     t.apply(d)
19726             );
19727         }
19728         
19729         
19730         
19731         el.update(html.join(""));
19732         this.nodes = el.dom.childNodes;
19733         this.updateIndexes(0);
19734     },
19735     
19736
19737     /**
19738      * Function to override to reformat the data that is sent to
19739      * the template for each node.
19740      * DEPRICATED - use the preparedata event handler.
19741      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19742      * a JSON object for an UpdateManager bound view).
19743      */
19744     prepareData : function(data, index, record)
19745     {
19746         this.fireEvent("preparedata", this, data, index, record);
19747         return data;
19748     },
19749
19750     onUpdate : function(ds, record){
19751         // Roo.log('on update');   
19752         this.clearSelections();
19753         var index = this.store.indexOf(record);
19754         var n = this.nodes[index];
19755         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19756         n.parentNode.removeChild(n);
19757         this.updateIndexes(index, index);
19758     },
19759
19760     
19761     
19762 // --------- FIXME     
19763     onAdd : function(ds, records, index)
19764     {
19765         //Roo.log(['on Add', ds, records, index] );        
19766         this.clearSelections();
19767         if(this.nodes.length == 0){
19768             this.refresh();
19769             return;
19770         }
19771         var n = this.nodes[index];
19772         for(var i = 0, len = records.length; i < len; i++){
19773             var d = this.prepareData(records[i].data, i, records[i]);
19774             if(n){
19775                 this.tpl.insertBefore(n, d);
19776             }else{
19777                 
19778                 this.tpl.append(this.el, d);
19779             }
19780         }
19781         this.updateIndexes(index);
19782     },
19783
19784     onRemove : function(ds, record, index){
19785        // Roo.log('onRemove');
19786         this.clearSelections();
19787         var el = this.dataName  ?
19788             this.el.child('.roo-tpl-' + this.dataName) :
19789             this.el; 
19790         
19791         el.dom.removeChild(this.nodes[index]);
19792         this.updateIndexes(index);
19793     },
19794
19795     /**
19796      * Refresh an individual node.
19797      * @param {Number} index
19798      */
19799     refreshNode : function(index){
19800         this.onUpdate(this.store, this.store.getAt(index));
19801     },
19802
19803     updateIndexes : function(startIndex, endIndex){
19804         var ns = this.nodes;
19805         startIndex = startIndex || 0;
19806         endIndex = endIndex || ns.length - 1;
19807         for(var i = startIndex; i <= endIndex; i++){
19808             ns[i].nodeIndex = i;
19809         }
19810     },
19811
19812     /**
19813      * Changes the data store this view uses and refresh the view.
19814      * @param {Store} store
19815      */
19816     setStore : function(store, initial){
19817         if(!initial && this.store){
19818             this.store.un("datachanged", this.refresh);
19819             this.store.un("add", this.onAdd);
19820             this.store.un("remove", this.onRemove);
19821             this.store.un("update", this.onUpdate);
19822             this.store.un("clear", this.refresh);
19823             this.store.un("beforeload", this.onBeforeLoad);
19824             this.store.un("load", this.onLoad);
19825             this.store.un("loadexception", this.onLoad);
19826         }
19827         if(store){
19828           
19829             store.on("datachanged", this.refresh, this);
19830             store.on("add", this.onAdd, this);
19831             store.on("remove", this.onRemove, this);
19832             store.on("update", this.onUpdate, this);
19833             store.on("clear", this.refresh, this);
19834             store.on("beforeload", this.onBeforeLoad, this);
19835             store.on("load", this.onLoad, this);
19836             store.on("loadexception", this.onLoad, this);
19837         }
19838         
19839         if(store){
19840             this.refresh();
19841         }
19842     },
19843     /**
19844      * onbeforeLoad - masks the loading area.
19845      *
19846      */
19847     onBeforeLoad : function(store,opts)
19848     {
19849          //Roo.log('onBeforeLoad');   
19850         if (!opts.add) {
19851             this.el.update("");
19852         }
19853         this.el.mask(this.mask ? this.mask : "Loading" ); 
19854     },
19855     onLoad : function ()
19856     {
19857         this.el.unmask();
19858     },
19859     
19860
19861     /**
19862      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19863      * @param {HTMLElement} node
19864      * @return {HTMLElement} The template node
19865      */
19866     findItemFromChild : function(node){
19867         var el = this.dataName  ?
19868             this.el.child('.roo-tpl-' + this.dataName,true) :
19869             this.el.dom; 
19870         
19871         if(!node || node.parentNode == el){
19872                     return node;
19873             }
19874             var p = node.parentNode;
19875             while(p && p != el){
19876             if(p.parentNode == el){
19877                 return p;
19878             }
19879             p = p.parentNode;
19880         }
19881             return null;
19882     },
19883
19884     /** @ignore */
19885     onClick : function(e){
19886         var item = this.findItemFromChild(e.getTarget());
19887         if(item){
19888             var index = this.indexOf(item);
19889             if(this.onItemClick(item, index, e) !== false){
19890                 this.fireEvent("click", this, index, item, e);
19891             }
19892         }else{
19893             this.clearSelections();
19894         }
19895     },
19896
19897     /** @ignore */
19898     onContextMenu : function(e){
19899         var item = this.findItemFromChild(e.getTarget());
19900         if(item){
19901             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19902         }
19903     },
19904
19905     /** @ignore */
19906     onDblClick : function(e){
19907         var item = this.findItemFromChild(e.getTarget());
19908         if(item){
19909             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19910         }
19911     },
19912
19913     onItemClick : function(item, index, e)
19914     {
19915         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19916             return false;
19917         }
19918         if (this.toggleSelect) {
19919             var m = this.isSelected(item) ? 'unselect' : 'select';
19920             //Roo.log(m);
19921             var _t = this;
19922             _t[m](item, true, false);
19923             return true;
19924         }
19925         if(this.multiSelect || this.singleSelect){
19926             if(this.multiSelect && e.shiftKey && this.lastSelection){
19927                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19928             }else{
19929                 this.select(item, this.multiSelect && e.ctrlKey);
19930                 this.lastSelection = item;
19931             }
19932             
19933             if(!this.tickable){
19934                 e.preventDefault();
19935             }
19936             
19937         }
19938         return true;
19939     },
19940
19941     /**
19942      * Get the number of selected nodes.
19943      * @return {Number}
19944      */
19945     getSelectionCount : function(){
19946         return this.selections.length;
19947     },
19948
19949     /**
19950      * Get the currently selected nodes.
19951      * @return {Array} An array of HTMLElements
19952      */
19953     getSelectedNodes : function(){
19954         return this.selections;
19955     },
19956
19957     /**
19958      * Get the indexes of the selected nodes.
19959      * @return {Array}
19960      */
19961     getSelectedIndexes : function(){
19962         var indexes = [], s = this.selections;
19963         for(var i = 0, len = s.length; i < len; i++){
19964             indexes.push(s[i].nodeIndex);
19965         }
19966         return indexes;
19967     },
19968
19969     /**
19970      * Clear all selections
19971      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19972      */
19973     clearSelections : function(suppressEvent){
19974         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19975             this.cmp.elements = this.selections;
19976             this.cmp.removeClass(this.selectedClass);
19977             this.selections = [];
19978             if(!suppressEvent){
19979                 this.fireEvent("selectionchange", this, this.selections);
19980             }
19981         }
19982     },
19983
19984     /**
19985      * Returns true if the passed node is selected
19986      * @param {HTMLElement/Number} node The node or node index
19987      * @return {Boolean}
19988      */
19989     isSelected : function(node){
19990         var s = this.selections;
19991         if(s.length < 1){
19992             return false;
19993         }
19994         node = this.getNode(node);
19995         return s.indexOf(node) !== -1;
19996     },
19997
19998     /**
19999      * Selects nodes.
20000      * @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
20001      * @param {Boolean} keepExisting (optional) true to keep existing selections
20002      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20003      */
20004     select : function(nodeInfo, keepExisting, suppressEvent){
20005         if(nodeInfo instanceof Array){
20006             if(!keepExisting){
20007                 this.clearSelections(true);
20008             }
20009             for(var i = 0, len = nodeInfo.length; i < len; i++){
20010                 this.select(nodeInfo[i], true, true);
20011             }
20012             return;
20013         } 
20014         var node = this.getNode(nodeInfo);
20015         if(!node || this.isSelected(node)){
20016             return; // already selected.
20017         }
20018         if(!keepExisting){
20019             this.clearSelections(true);
20020         }
20021         
20022         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20023             Roo.fly(node).addClass(this.selectedClass);
20024             this.selections.push(node);
20025             if(!suppressEvent){
20026                 this.fireEvent("selectionchange", this, this.selections);
20027             }
20028         }
20029         
20030         
20031     },
20032       /**
20033      * Unselects nodes.
20034      * @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
20035      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20036      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20037      */
20038     unselect : function(nodeInfo, keepExisting, suppressEvent)
20039     {
20040         if(nodeInfo instanceof Array){
20041             Roo.each(this.selections, function(s) {
20042                 this.unselect(s, nodeInfo);
20043             }, this);
20044             return;
20045         }
20046         var node = this.getNode(nodeInfo);
20047         if(!node || !this.isSelected(node)){
20048             //Roo.log("not selected");
20049             return; // not selected.
20050         }
20051         // fireevent???
20052         var ns = [];
20053         Roo.each(this.selections, function(s) {
20054             if (s == node ) {
20055                 Roo.fly(node).removeClass(this.selectedClass);
20056
20057                 return;
20058             }
20059             ns.push(s);
20060         },this);
20061         
20062         this.selections= ns;
20063         this.fireEvent("selectionchange", this, this.selections);
20064     },
20065
20066     /**
20067      * Gets a template node.
20068      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20069      * @return {HTMLElement} The node or null if it wasn't found
20070      */
20071     getNode : function(nodeInfo){
20072         if(typeof nodeInfo == "string"){
20073             return document.getElementById(nodeInfo);
20074         }else if(typeof nodeInfo == "number"){
20075             return this.nodes[nodeInfo];
20076         }
20077         return nodeInfo;
20078     },
20079
20080     /**
20081      * Gets a range template nodes.
20082      * @param {Number} startIndex
20083      * @param {Number} endIndex
20084      * @return {Array} An array of nodes
20085      */
20086     getNodes : function(start, end){
20087         var ns = this.nodes;
20088         start = start || 0;
20089         end = typeof end == "undefined" ? ns.length - 1 : end;
20090         var nodes = [];
20091         if(start <= end){
20092             for(var i = start; i <= end; i++){
20093                 nodes.push(ns[i]);
20094             }
20095         } else{
20096             for(var i = start; i >= end; i--){
20097                 nodes.push(ns[i]);
20098             }
20099         }
20100         return nodes;
20101     },
20102
20103     /**
20104      * Finds the index of the passed node
20105      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20106      * @return {Number} The index of the node or -1
20107      */
20108     indexOf : function(node){
20109         node = this.getNode(node);
20110         if(typeof node.nodeIndex == "number"){
20111             return node.nodeIndex;
20112         }
20113         var ns = this.nodes;
20114         for(var i = 0, len = ns.length; i < len; i++){
20115             if(ns[i] == node){
20116                 return i;
20117             }
20118         }
20119         return -1;
20120     }
20121 });
20122 /*
20123  * - LGPL
20124  *
20125  * based on jquery fullcalendar
20126  * 
20127  */
20128
20129 Roo.bootstrap = Roo.bootstrap || {};
20130 /**
20131  * @class Roo.bootstrap.Calendar
20132  * @extends Roo.bootstrap.Component
20133  * Bootstrap Calendar class
20134  * @cfg {Boolean} loadMask (true|false) default false
20135  * @cfg {Object} header generate the user specific header of the calendar, default false
20136
20137  * @constructor
20138  * Create a new Container
20139  * @param {Object} config The config object
20140  */
20141
20142
20143
20144 Roo.bootstrap.Calendar = function(config){
20145     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20146      this.addEvents({
20147         /**
20148              * @event select
20149              * Fires when a date is selected
20150              * @param {DatePicker} this
20151              * @param {Date} date The selected date
20152              */
20153         'select': true,
20154         /**
20155              * @event monthchange
20156              * Fires when the displayed month changes 
20157              * @param {DatePicker} this
20158              * @param {Date} date The selected month
20159              */
20160         'monthchange': true,
20161         /**
20162              * @event evententer
20163              * Fires when mouse over an event
20164              * @param {Calendar} this
20165              * @param {event} Event
20166              */
20167         'evententer': true,
20168         /**
20169              * @event eventleave
20170              * Fires when the mouse leaves an
20171              * @param {Calendar} this
20172              * @param {event}
20173              */
20174         'eventleave': true,
20175         /**
20176              * @event eventclick
20177              * Fires when the mouse click an
20178              * @param {Calendar} this
20179              * @param {event}
20180              */
20181         'eventclick': true
20182         
20183     });
20184
20185 };
20186
20187 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20188     
20189      /**
20190      * @cfg {Number} startDay
20191      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20192      */
20193     startDay : 0,
20194     
20195     loadMask : false,
20196     
20197     header : false,
20198       
20199     getAutoCreate : function(){
20200         
20201         
20202         var fc_button = function(name, corner, style, content ) {
20203             return Roo.apply({},{
20204                 tag : 'span',
20205                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20206                          (corner.length ?
20207                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20208                             ''
20209                         ),
20210                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20211                 unselectable: 'on'
20212             });
20213         };
20214         
20215         var header = {};
20216         
20217         if(!this.header){
20218             header = {
20219                 tag : 'table',
20220                 cls : 'fc-header',
20221                 style : 'width:100%',
20222                 cn : [
20223                     {
20224                         tag: 'tr',
20225                         cn : [
20226                             {
20227                                 tag : 'td',
20228                                 cls : 'fc-header-left',
20229                                 cn : [
20230                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20231                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20232                                     { tag: 'span', cls: 'fc-header-space' },
20233                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20234
20235
20236                                 ]
20237                             },
20238
20239                             {
20240                                 tag : 'td',
20241                                 cls : 'fc-header-center',
20242                                 cn : [
20243                                     {
20244                                         tag: 'span',
20245                                         cls: 'fc-header-title',
20246                                         cn : {
20247                                             tag: 'H2',
20248                                             html : 'month / year'
20249                                         }
20250                                     }
20251
20252                                 ]
20253                             },
20254                             {
20255                                 tag : 'td',
20256                                 cls : 'fc-header-right',
20257                                 cn : [
20258                               /*      fc_button('month', 'left', '', 'month' ),
20259                                     fc_button('week', '', '', 'week' ),
20260                                     fc_button('day', 'right', '', 'day' )
20261                                 */    
20262
20263                                 ]
20264                             }
20265
20266                         ]
20267                     }
20268                 ]
20269             };
20270         }
20271         
20272         header = this.header;
20273         
20274        
20275         var cal_heads = function() {
20276             var ret = [];
20277             // fixme - handle this.
20278             
20279             for (var i =0; i < Date.dayNames.length; i++) {
20280                 var d = Date.dayNames[i];
20281                 ret.push({
20282                     tag: 'th',
20283                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20284                     html : d.substring(0,3)
20285                 });
20286                 
20287             }
20288             ret[0].cls += ' fc-first';
20289             ret[6].cls += ' fc-last';
20290             return ret;
20291         };
20292         var cal_cell = function(n) {
20293             return  {
20294                 tag: 'td',
20295                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20296                 cn : [
20297                     {
20298                         cn : [
20299                             {
20300                                 cls: 'fc-day-number',
20301                                 html: 'D'
20302                             },
20303                             {
20304                                 cls: 'fc-day-content',
20305                              
20306                                 cn : [
20307                                      {
20308                                         style: 'position: relative;' // height: 17px;
20309                                     }
20310                                 ]
20311                             }
20312                             
20313                             
20314                         ]
20315                     }
20316                 ]
20317                 
20318             }
20319         };
20320         var cal_rows = function() {
20321             
20322             var ret = [];
20323             for (var r = 0; r < 6; r++) {
20324                 var row= {
20325                     tag : 'tr',
20326                     cls : 'fc-week',
20327                     cn : []
20328                 };
20329                 
20330                 for (var i =0; i < Date.dayNames.length; i++) {
20331                     var d = Date.dayNames[i];
20332                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20333
20334                 }
20335                 row.cn[0].cls+=' fc-first';
20336                 row.cn[0].cn[0].style = 'min-height:90px';
20337                 row.cn[6].cls+=' fc-last';
20338                 ret.push(row);
20339                 
20340             }
20341             ret[0].cls += ' fc-first';
20342             ret[4].cls += ' fc-prev-last';
20343             ret[5].cls += ' fc-last';
20344             return ret;
20345             
20346         };
20347         
20348         var cal_table = {
20349             tag: 'table',
20350             cls: 'fc-border-separate',
20351             style : 'width:100%',
20352             cellspacing  : 0,
20353             cn : [
20354                 { 
20355                     tag: 'thead',
20356                     cn : [
20357                         { 
20358                             tag: 'tr',
20359                             cls : 'fc-first fc-last',
20360                             cn : cal_heads()
20361                         }
20362                     ]
20363                 },
20364                 { 
20365                     tag: 'tbody',
20366                     cn : cal_rows()
20367                 }
20368                   
20369             ]
20370         };
20371          
20372          var cfg = {
20373             cls : 'fc fc-ltr',
20374             cn : [
20375                 header,
20376                 {
20377                     cls : 'fc-content',
20378                     style : "position: relative;",
20379                     cn : [
20380                         {
20381                             cls : 'fc-view fc-view-month fc-grid',
20382                             style : 'position: relative',
20383                             unselectable : 'on',
20384                             cn : [
20385                                 {
20386                                     cls : 'fc-event-container',
20387                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20388                                 },
20389                                 cal_table
20390                             ]
20391                         }
20392                     ]
20393     
20394                 }
20395            ] 
20396             
20397         };
20398         
20399          
20400         
20401         return cfg;
20402     },
20403     
20404     
20405     initEvents : function()
20406     {
20407         if(!this.store){
20408             throw "can not find store for calendar";
20409         }
20410         
20411         var mark = {
20412             tag: "div",
20413             cls:"x-dlg-mask",
20414             style: "text-align:center",
20415             cn: [
20416                 {
20417                     tag: "div",
20418                     style: "background-color:white;width:50%;margin:250 auto",
20419                     cn: [
20420                         {
20421                             tag: "img",
20422                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20423                         },
20424                         {
20425                             tag: "span",
20426                             html: "Loading"
20427                         }
20428                         
20429                     ]
20430                 }
20431             ]
20432         };
20433         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20434         
20435         var size = this.el.select('.fc-content', true).first().getSize();
20436         this.maskEl.setSize(size.width, size.height);
20437         this.maskEl.enableDisplayMode("block");
20438         if(!this.loadMask){
20439             this.maskEl.hide();
20440         }
20441         
20442         this.store = Roo.factory(this.store, Roo.data);
20443         this.store.on('load', this.onLoad, this);
20444         this.store.on('beforeload', this.onBeforeLoad, this);
20445         
20446         this.resize();
20447         
20448         this.cells = this.el.select('.fc-day',true);
20449         //Roo.log(this.cells);
20450         this.textNodes = this.el.query('.fc-day-number');
20451         this.cells.addClassOnOver('fc-state-hover');
20452         
20453         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20454         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20455         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20456         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20457         
20458         this.on('monthchange', this.onMonthChange, this);
20459         
20460         this.update(new Date().clearTime());
20461     },
20462     
20463     resize : function() {
20464         var sz  = this.el.getSize();
20465         
20466         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20467         this.el.select('.fc-day-content div',true).setHeight(34);
20468     },
20469     
20470     
20471     // private
20472     showPrevMonth : function(e){
20473         this.update(this.activeDate.add("mo", -1));
20474     },
20475     showToday : function(e){
20476         this.update(new Date().clearTime());
20477     },
20478     // private
20479     showNextMonth : function(e){
20480         this.update(this.activeDate.add("mo", 1));
20481     },
20482
20483     // private
20484     showPrevYear : function(){
20485         this.update(this.activeDate.add("y", -1));
20486     },
20487
20488     // private
20489     showNextYear : function(){
20490         this.update(this.activeDate.add("y", 1));
20491     },
20492
20493     
20494    // private
20495     update : function(date)
20496     {
20497         var vd = this.activeDate;
20498         this.activeDate = date;
20499 //        if(vd && this.el){
20500 //            var t = date.getTime();
20501 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20502 //                Roo.log('using add remove');
20503 //                
20504 //                this.fireEvent('monthchange', this, date);
20505 //                
20506 //                this.cells.removeClass("fc-state-highlight");
20507 //                this.cells.each(function(c){
20508 //                   if(c.dateValue == t){
20509 //                       c.addClass("fc-state-highlight");
20510 //                       setTimeout(function(){
20511 //                            try{c.dom.firstChild.focus();}catch(e){}
20512 //                       }, 50);
20513 //                       return false;
20514 //                   }
20515 //                   return true;
20516 //                });
20517 //                return;
20518 //            }
20519 //        }
20520         
20521         var days = date.getDaysInMonth();
20522         
20523         var firstOfMonth = date.getFirstDateOfMonth();
20524         var startingPos = firstOfMonth.getDay()-this.startDay;
20525         
20526         if(startingPos < this.startDay){
20527             startingPos += 7;
20528         }
20529         
20530         var pm = date.add(Date.MONTH, -1);
20531         var prevStart = pm.getDaysInMonth()-startingPos;
20532 //        
20533         this.cells = this.el.select('.fc-day',true);
20534         this.textNodes = this.el.query('.fc-day-number');
20535         this.cells.addClassOnOver('fc-state-hover');
20536         
20537         var cells = this.cells.elements;
20538         var textEls = this.textNodes;
20539         
20540         Roo.each(cells, function(cell){
20541             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20542         });
20543         
20544         days += startingPos;
20545
20546         // convert everything to numbers so it's fast
20547         var day = 86400000;
20548         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20549         //Roo.log(d);
20550         //Roo.log(pm);
20551         //Roo.log(prevStart);
20552         
20553         var today = new Date().clearTime().getTime();
20554         var sel = date.clearTime().getTime();
20555         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20556         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20557         var ddMatch = this.disabledDatesRE;
20558         var ddText = this.disabledDatesText;
20559         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20560         var ddaysText = this.disabledDaysText;
20561         var format = this.format;
20562         
20563         var setCellClass = function(cal, cell){
20564             cell.row = 0;
20565             cell.events = [];
20566             cell.more = [];
20567             //Roo.log('set Cell Class');
20568             cell.title = "";
20569             var t = d.getTime();
20570             
20571             //Roo.log(d);
20572             
20573             cell.dateValue = t;
20574             if(t == today){
20575                 cell.className += " fc-today";
20576                 cell.className += " fc-state-highlight";
20577                 cell.title = cal.todayText;
20578             }
20579             if(t == sel){
20580                 // disable highlight in other month..
20581                 //cell.className += " fc-state-highlight";
20582                 
20583             }
20584             // disabling
20585             if(t < min) {
20586                 cell.className = " fc-state-disabled";
20587                 cell.title = cal.minText;
20588                 return;
20589             }
20590             if(t > max) {
20591                 cell.className = " fc-state-disabled";
20592                 cell.title = cal.maxText;
20593                 return;
20594             }
20595             if(ddays){
20596                 if(ddays.indexOf(d.getDay()) != -1){
20597                     cell.title = ddaysText;
20598                     cell.className = " fc-state-disabled";
20599                 }
20600             }
20601             if(ddMatch && format){
20602                 var fvalue = d.dateFormat(format);
20603                 if(ddMatch.test(fvalue)){
20604                     cell.title = ddText.replace("%0", fvalue);
20605                     cell.className = " fc-state-disabled";
20606                 }
20607             }
20608             
20609             if (!cell.initialClassName) {
20610                 cell.initialClassName = cell.dom.className;
20611             }
20612             
20613             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20614         };
20615
20616         var i = 0;
20617         
20618         for(; i < startingPos; i++) {
20619             textEls[i].innerHTML = (++prevStart);
20620             d.setDate(d.getDate()+1);
20621             
20622             cells[i].className = "fc-past fc-other-month";
20623             setCellClass(this, cells[i]);
20624         }
20625         
20626         var intDay = 0;
20627         
20628         for(; i < days; i++){
20629             intDay = i - startingPos + 1;
20630             textEls[i].innerHTML = (intDay);
20631             d.setDate(d.getDate()+1);
20632             
20633             cells[i].className = ''; // "x-date-active";
20634             setCellClass(this, cells[i]);
20635         }
20636         var extraDays = 0;
20637         
20638         for(; i < 42; i++) {
20639             textEls[i].innerHTML = (++extraDays);
20640             d.setDate(d.getDate()+1);
20641             
20642             cells[i].className = "fc-future fc-other-month";
20643             setCellClass(this, cells[i]);
20644         }
20645         
20646         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20647         
20648         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20649         
20650         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20651         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20652         
20653         if(totalRows != 6){
20654             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20655             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20656         }
20657         
20658         this.fireEvent('monthchange', this, date);
20659         
20660         
20661         /*
20662         if(!this.internalRender){
20663             var main = this.el.dom.firstChild;
20664             var w = main.offsetWidth;
20665             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20666             Roo.fly(main).setWidth(w);
20667             this.internalRender = true;
20668             // opera does not respect the auto grow header center column
20669             // then, after it gets a width opera refuses to recalculate
20670             // without a second pass
20671             if(Roo.isOpera && !this.secondPass){
20672                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20673                 this.secondPass = true;
20674                 this.update.defer(10, this, [date]);
20675             }
20676         }
20677         */
20678         
20679     },
20680     
20681     findCell : function(dt) {
20682         dt = dt.clearTime().getTime();
20683         var ret = false;
20684         this.cells.each(function(c){
20685             //Roo.log("check " +c.dateValue + '?=' + dt);
20686             if(c.dateValue == dt){
20687                 ret = c;
20688                 return false;
20689             }
20690             return true;
20691         });
20692         
20693         return ret;
20694     },
20695     
20696     findCells : function(ev) {
20697         var s = ev.start.clone().clearTime().getTime();
20698        // Roo.log(s);
20699         var e= ev.end.clone().clearTime().getTime();
20700        // Roo.log(e);
20701         var ret = [];
20702         this.cells.each(function(c){
20703              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20704             
20705             if(c.dateValue > e){
20706                 return ;
20707             }
20708             if(c.dateValue < s){
20709                 return ;
20710             }
20711             ret.push(c);
20712         });
20713         
20714         return ret;    
20715     },
20716     
20717 //    findBestRow: function(cells)
20718 //    {
20719 //        var ret = 0;
20720 //        
20721 //        for (var i =0 ; i < cells.length;i++) {
20722 //            ret  = Math.max(cells[i].rows || 0,ret);
20723 //        }
20724 //        return ret;
20725 //        
20726 //    },
20727     
20728     
20729     addItem : function(ev)
20730     {
20731         // look for vertical location slot in
20732         var cells = this.findCells(ev);
20733         
20734 //        ev.row = this.findBestRow(cells);
20735         
20736         // work out the location.
20737         
20738         var crow = false;
20739         var rows = [];
20740         for(var i =0; i < cells.length; i++) {
20741             
20742             cells[i].row = cells[0].row;
20743             
20744             if(i == 0){
20745                 cells[i].row = cells[i].row + 1;
20746             }
20747             
20748             if (!crow) {
20749                 crow = {
20750                     start : cells[i],
20751                     end :  cells[i]
20752                 };
20753                 continue;
20754             }
20755             if (crow.start.getY() == cells[i].getY()) {
20756                 // on same row.
20757                 crow.end = cells[i];
20758                 continue;
20759             }
20760             // different row.
20761             rows.push(crow);
20762             crow = {
20763                 start: cells[i],
20764                 end : cells[i]
20765             };
20766             
20767         }
20768         
20769         rows.push(crow);
20770         ev.els = [];
20771         ev.rows = rows;
20772         ev.cells = cells;
20773         
20774         cells[0].events.push(ev);
20775         
20776         this.calevents.push(ev);
20777     },
20778     
20779     clearEvents: function() {
20780         
20781         if(!this.calevents){
20782             return;
20783         }
20784         
20785         Roo.each(this.cells.elements, function(c){
20786             c.row = 0;
20787             c.events = [];
20788             c.more = [];
20789         });
20790         
20791         Roo.each(this.calevents, function(e) {
20792             Roo.each(e.els, function(el) {
20793                 el.un('mouseenter' ,this.onEventEnter, this);
20794                 el.un('mouseleave' ,this.onEventLeave, this);
20795                 el.remove();
20796             },this);
20797         },this);
20798         
20799         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20800             e.remove();
20801         });
20802         
20803     },
20804     
20805     renderEvents: function()
20806     {   
20807         var _this = this;
20808         
20809         this.cells.each(function(c) {
20810             
20811             if(c.row < 5){
20812                 return;
20813             }
20814             
20815             var ev = c.events;
20816             
20817             var r = 4;
20818             if(c.row != c.events.length){
20819                 r = 4 - (4 - (c.row - c.events.length));
20820             }
20821             
20822             c.events = ev.slice(0, r);
20823             c.more = ev.slice(r);
20824             
20825             if(c.more.length && c.more.length == 1){
20826                 c.events.push(c.more.pop());
20827             }
20828             
20829             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20830             
20831         });
20832             
20833         this.cells.each(function(c) {
20834             
20835             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20836             
20837             
20838             for (var e = 0; e < c.events.length; e++){
20839                 var ev = c.events[e];
20840                 var rows = ev.rows;
20841                 
20842                 for(var i = 0; i < rows.length; i++) {
20843                 
20844                     // how many rows should it span..
20845
20846                     var  cfg = {
20847                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20848                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20849
20850                         unselectable : "on",
20851                         cn : [
20852                             {
20853                                 cls: 'fc-event-inner',
20854                                 cn : [
20855     //                                {
20856     //                                  tag:'span',
20857     //                                  cls: 'fc-event-time',
20858     //                                  html : cells.length > 1 ? '' : ev.time
20859     //                                },
20860                                     {
20861                                       tag:'span',
20862                                       cls: 'fc-event-title',
20863                                       html : String.format('{0}', ev.title)
20864                                     }
20865
20866
20867                                 ]
20868                             },
20869                             {
20870                                 cls: 'ui-resizable-handle ui-resizable-e',
20871                                 html : '&nbsp;&nbsp;&nbsp'
20872                             }
20873
20874                         ]
20875                     };
20876
20877                     if (i == 0) {
20878                         cfg.cls += ' fc-event-start';
20879                     }
20880                     if ((i+1) == rows.length) {
20881                         cfg.cls += ' fc-event-end';
20882                     }
20883
20884                     var ctr = _this.el.select('.fc-event-container',true).first();
20885                     var cg = ctr.createChild(cfg);
20886
20887                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20888                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20889
20890                     var r = (c.more.length) ? 1 : 0;
20891                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20892                     cg.setWidth(ebox.right - sbox.x -2);
20893
20894                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20895                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20896                     cg.on('click', _this.onEventClick, _this, ev);
20897
20898                     ev.els.push(cg);
20899                     
20900                 }
20901                 
20902             }
20903             
20904             
20905             if(c.more.length){
20906                 var  cfg = {
20907                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20908                     style : 'position: absolute',
20909                     unselectable : "on",
20910                     cn : [
20911                         {
20912                             cls: 'fc-event-inner',
20913                             cn : [
20914                                 {
20915                                   tag:'span',
20916                                   cls: 'fc-event-title',
20917                                   html : 'More'
20918                                 }
20919
20920
20921                             ]
20922                         },
20923                         {
20924                             cls: 'ui-resizable-handle ui-resizable-e',
20925                             html : '&nbsp;&nbsp;&nbsp'
20926                         }
20927
20928                     ]
20929                 };
20930
20931                 var ctr = _this.el.select('.fc-event-container',true).first();
20932                 var cg = ctr.createChild(cfg);
20933
20934                 var sbox = c.select('.fc-day-content',true).first().getBox();
20935                 var ebox = c.select('.fc-day-content',true).first().getBox();
20936                 //Roo.log(cg);
20937                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20938                 cg.setWidth(ebox.right - sbox.x -2);
20939
20940                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20941                 
20942             }
20943             
20944         });
20945         
20946         
20947         
20948     },
20949     
20950     onEventEnter: function (e, el,event,d) {
20951         this.fireEvent('evententer', this, el, event);
20952     },
20953     
20954     onEventLeave: function (e, el,event,d) {
20955         this.fireEvent('eventleave', this, el, event);
20956     },
20957     
20958     onEventClick: function (e, el,event,d) {
20959         this.fireEvent('eventclick', this, el, event);
20960     },
20961     
20962     onMonthChange: function () {
20963         this.store.load();
20964     },
20965     
20966     onMoreEventClick: function(e, el, more)
20967     {
20968         var _this = this;
20969         
20970         this.calpopover.placement = 'right';
20971         this.calpopover.setTitle('More');
20972         
20973         this.calpopover.setContent('');
20974         
20975         var ctr = this.calpopover.el.select('.popover-content', true).first();
20976         
20977         Roo.each(more, function(m){
20978             var cfg = {
20979                 cls : 'fc-event-hori fc-event-draggable',
20980                 html : m.title
20981             };
20982             var cg = ctr.createChild(cfg);
20983             
20984             cg.on('click', _this.onEventClick, _this, m);
20985         });
20986         
20987         this.calpopover.show(el);
20988         
20989         
20990     },
20991     
20992     onLoad: function () 
20993     {   
20994         this.calevents = [];
20995         var cal = this;
20996         
20997         if(this.store.getCount() > 0){
20998             this.store.data.each(function(d){
20999                cal.addItem({
21000                     id : d.data.id,
21001                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21002                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21003                     time : d.data.start_time,
21004                     title : d.data.title,
21005                     description : d.data.description,
21006                     venue : d.data.venue
21007                 });
21008             });
21009         }
21010         
21011         this.renderEvents();
21012         
21013         if(this.calevents.length && this.loadMask){
21014             this.maskEl.hide();
21015         }
21016     },
21017     
21018     onBeforeLoad: function()
21019     {
21020         this.clearEvents();
21021         if(this.loadMask){
21022             this.maskEl.show();
21023         }
21024     }
21025 });
21026
21027  
21028  /*
21029  * - LGPL
21030  *
21031  * element
21032  * 
21033  */
21034
21035 /**
21036  * @class Roo.bootstrap.Popover
21037  * @extends Roo.bootstrap.Component
21038  * Bootstrap Popover class
21039  * @cfg {String} html contents of the popover   (or false to use children..)
21040  * @cfg {String} title of popover (or false to hide)
21041  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21042  * @cfg {String} trigger click || hover (or false to trigger manually)
21043  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21044  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21045  *      - if false and it has a 'parent' then it will be automatically added to that element
21046  *      - if string - Roo.get  will be called 
21047  * @cfg {Number} delay - delay before showing
21048  
21049  * @constructor
21050  * Create a new Popover
21051  * @param {Object} config The config object
21052  */
21053
21054 Roo.bootstrap.Popover = function(config){
21055     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21056     
21057     this.addEvents({
21058         // raw events
21059          /**
21060          * @event show
21061          * After the popover show
21062          * 
21063          * @param {Roo.bootstrap.Popover} this
21064          */
21065         "show" : true,
21066         /**
21067          * @event hide
21068          * After the popover hide
21069          * 
21070          * @param {Roo.bootstrap.Popover} this
21071          */
21072         "hide" : true
21073     });
21074 };
21075
21076 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21077     
21078     title: false,
21079     html: false,
21080     
21081     placement : 'right',
21082     trigger : 'hover', // hover
21083     modal : false,
21084     delay : 0,
21085     
21086     over: false,
21087     
21088     can_build_overlaid : false,
21089     
21090     maskEl : false, // the mask element
21091     headerEl : false,
21092     contentEl : false,
21093     alignEl : false, // when show is called with an element - this get's stored.
21094     
21095     getChildContainer : function()
21096     {
21097         return this.contentEl;
21098         
21099     },
21100     getPopoverHeader : function()
21101     {
21102         this.title = true; // flag not to hide it..
21103         this.headerEl.addClass('p-0');
21104         return this.headerEl
21105     },
21106     
21107     
21108     getAutoCreate : function(){
21109          
21110         var cfg = {
21111            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21112            style: 'display:block',
21113            cn : [
21114                 {
21115                     cls : 'arrow'
21116                 },
21117                 {
21118                     cls : 'popover-inner ',
21119                     cn : [
21120                         {
21121                             tag: 'h3',
21122                             cls: 'popover-title popover-header',
21123                             html : this.title === false ? '' : this.title
21124                         },
21125                         {
21126                             cls : 'popover-content popover-body '  + (this.cls || ''),
21127                             html : this.html || ''
21128                         }
21129                     ]
21130                     
21131                 }
21132            ]
21133         };
21134         
21135         return cfg;
21136     },
21137     /**
21138      * @param {string} the title
21139      */
21140     setTitle: function(str)
21141     {
21142         this.title = str;
21143         if (this.el) {
21144             this.headerEl.dom.innerHTML = str;
21145         }
21146         
21147     },
21148     /**
21149      * @param {string} the body content
21150      */
21151     setContent: function(str)
21152     {
21153         this.html = str;
21154         if (this.contentEl) {
21155             this.contentEl.dom.innerHTML = str;
21156         }
21157         
21158     },
21159     // as it get's added to the bottom of the page.
21160     onRender : function(ct, position)
21161     {
21162         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21163         
21164         
21165         
21166         if(!this.el){
21167             var cfg = Roo.apply({},  this.getAutoCreate());
21168             cfg.id = Roo.id();
21169             
21170             if (this.cls) {
21171                 cfg.cls += ' ' + this.cls;
21172             }
21173             if (this.style) {
21174                 cfg.style = this.style;
21175             }
21176             //Roo.log("adding to ");
21177             this.el = Roo.get(document.body).createChild(cfg, position);
21178 //            Roo.log(this.el);
21179         }
21180         
21181         this.contentEl = this.el.select('.popover-content',true).first();
21182         this.headerEl =  this.el.select('.popover-title',true).first();
21183         
21184         var nitems = [];
21185         if(typeof(this.items) != 'undefined'){
21186             var items = this.items;
21187             delete this.items;
21188
21189             for(var i =0;i < items.length;i++) {
21190                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21191             }
21192         }
21193
21194         this.items = nitems;
21195         
21196         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21197         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21198         
21199         
21200         
21201         this.initEvents();
21202     },
21203     
21204     resizeMask : function()
21205     {
21206         this.maskEl.setSize(
21207             Roo.lib.Dom.getViewWidth(true),
21208             Roo.lib.Dom.getViewHeight(true)
21209         );
21210     },
21211     
21212     initEvents : function()
21213     {
21214         
21215         if (!this.modal) { 
21216             Roo.bootstrap.Popover.register(this);
21217         }
21218          
21219         this.arrowEl = this.el.select('.arrow',true).first();
21220         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21221         this.el.enableDisplayMode('block');
21222         this.el.hide();
21223  
21224         
21225         if (this.over === false && !this.parent()) {
21226             return; 
21227         }
21228         if (this.triggers === false) {
21229             return;
21230         }
21231          
21232         // support parent
21233         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21234         var triggers = this.trigger ? this.trigger.split(' ') : [];
21235         Roo.each(triggers, function(trigger) {
21236         
21237             if (trigger == 'click') {
21238                 on_el.on('click', this.toggle, this);
21239             } else if (trigger != 'manual') {
21240                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21241                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21242       
21243                 on_el.on(eventIn  ,this.enter, this);
21244                 on_el.on(eventOut, this.leave, this);
21245             }
21246         }, this);
21247     },
21248     
21249     
21250     // private
21251     timeout : null,
21252     hoverState : null,
21253     
21254     toggle : function () {
21255         this.hoverState == 'in' ? this.leave() : this.enter();
21256     },
21257     
21258     enter : function () {
21259         
21260         clearTimeout(this.timeout);
21261     
21262         this.hoverState = 'in';
21263     
21264         if (!this.delay || !this.delay.show) {
21265             this.show();
21266             return;
21267         }
21268         var _t = this;
21269         this.timeout = setTimeout(function () {
21270             if (_t.hoverState == 'in') {
21271                 _t.show();
21272             }
21273         }, this.delay.show)
21274     },
21275     
21276     leave : function() {
21277         clearTimeout(this.timeout);
21278     
21279         this.hoverState = 'out';
21280     
21281         if (!this.delay || !this.delay.hide) {
21282             this.hide();
21283             return;
21284         }
21285         var _t = this;
21286         this.timeout = setTimeout(function () {
21287             if (_t.hoverState == 'out') {
21288                 _t.hide();
21289             }
21290         }, this.delay.hide)
21291     },
21292     /**
21293      * Show the popover
21294      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21295      * @param {string} (left|right|top|bottom) position
21296      */
21297     show : function (on_el, placement)
21298     {
21299         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21300         on_el = on_el || false; // default to false
21301          
21302         if (!on_el) {
21303             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21304                 on_el = this.parent().el;
21305             } else if (this.over) {
21306                 on_el = Roo.get(this.over);
21307             }
21308             
21309         }
21310         
21311         this.alignEl = Roo.get( on_el );
21312
21313         if (!this.el) {
21314             this.render(document.body);
21315         }
21316         
21317         
21318          
21319         
21320         if (this.title === false) {
21321             this.headerEl.hide();
21322         }
21323         
21324        
21325         this.el.show();
21326         this.el.dom.style.display = 'block';
21327          
21328  
21329         if (this.alignEl) {
21330             this.updatePosition(this.placement, true);
21331              
21332         } else {
21333             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21334             var es = this.el.getSize();
21335             var x = Roo.lib.Dom.getViewWidth()/2;
21336             var y = Roo.lib.Dom.getViewHeight()/2;
21337             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21338             
21339         }
21340
21341         
21342         //var arrow = this.el.select('.arrow',true).first();
21343         //arrow.set(align[2], 
21344         
21345         this.el.addClass('in');
21346         
21347          
21348         
21349         this.hoverState = 'in';
21350         
21351         if (this.modal) {
21352             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21353             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21354             this.maskEl.dom.style.display = 'block';
21355             this.maskEl.addClass('show');
21356         }
21357         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21358  
21359         this.fireEvent('show', this);
21360         
21361     },
21362     /**
21363      * fire this manually after loading a grid in the table for example
21364      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21365      * @param {Boolean} try and move it if we cant get right position.
21366      */
21367     updatePosition : function(placement, try_move)
21368     {
21369         // allow for calling with no parameters
21370         placement = placement   ? placement :  this.placement;
21371         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21372         
21373         this.el.removeClass([
21374             'fade','top','bottom', 'left', 'right','in',
21375             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21376         ]);
21377         this.el.addClass(placement + ' bs-popover-' + placement);
21378         
21379         if (!this.alignEl ) {
21380             return false;
21381         }
21382         
21383         switch (placement) {
21384             case 'right':
21385                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21386                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21387                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21388                     //normal display... or moved up/down.
21389                     this.el.setXY(offset);
21390                     var xy = this.alignEl.getAnchorXY('tr', false);
21391                     xy[0]+=2;xy[1]+=5;
21392                     this.arrowEl.setXY(xy);
21393                     return true;
21394                 }
21395                 // continue through...
21396                 return this.updatePosition('left', false);
21397                 
21398             
21399             case 'left':
21400                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21401                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21402                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21403                     //normal display... or moved up/down.
21404                     this.el.setXY(offset);
21405                     var xy = this.alignEl.getAnchorXY('tl', false);
21406                     xy[0]-=10;xy[1]+=5; // << fix me
21407                     this.arrowEl.setXY(xy);
21408                     return true;
21409                 }
21410                 // call self...
21411                 return this.updatePosition('right', false);
21412             
21413             case 'top':
21414                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21415                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21416                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21417                     //normal display... or moved up/down.
21418                     this.el.setXY(offset);
21419                     var xy = this.alignEl.getAnchorXY('t', false);
21420                     xy[1]-=10; // << fix me
21421                     this.arrowEl.setXY(xy);
21422                     return true;
21423                 }
21424                 // fall through
21425                return this.updatePosition('bottom', false);
21426             
21427             case 'bottom':
21428                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21429                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21430                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21431                     //normal display... or moved up/down.
21432                     this.el.setXY(offset);
21433                     var xy = this.alignEl.getAnchorXY('b', false);
21434                      xy[1]+=2; // << fix me
21435                     this.arrowEl.setXY(xy);
21436                     return true;
21437                 }
21438                 // fall through
21439                 return this.updatePosition('top', false);
21440                 
21441             
21442         }
21443         
21444         
21445         return false;
21446     },
21447     
21448     hide : function()
21449     {
21450         this.el.setXY([0,0]);
21451         this.el.removeClass('in');
21452         this.el.hide();
21453         this.hoverState = null;
21454         this.maskEl.hide(); // always..
21455         this.fireEvent('hide', this);
21456     }
21457     
21458 });
21459
21460
21461 Roo.apply(Roo.bootstrap.Popover, {
21462
21463     alignment : {
21464         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21465         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21466         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21467         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21468     },
21469     
21470     zIndex : 20001,
21471
21472     clickHander : false,
21473     
21474     
21475
21476     onMouseDown : function(e)
21477     {
21478         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21479             /// what is nothing is showing..
21480             this.hideAll();
21481         }
21482          
21483     },
21484     
21485     
21486     popups : [],
21487     
21488     register : function(popup)
21489     {
21490         if (!Roo.bootstrap.Popover.clickHandler) {
21491             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21492         }
21493         // hide other popups.
21494         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21495         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21496         this.hideAll(); //<< why?
21497         //this.popups.push(popup);
21498     },
21499     hideAll : function()
21500     {
21501         this.popups.forEach(function(p) {
21502             p.hide();
21503         });
21504     },
21505     onShow : function() {
21506         Roo.bootstrap.Popover.popups.push(this);
21507     },
21508     onHide : function() {
21509         Roo.bootstrap.Popover.popups.remove(this);
21510     } 
21511
21512 });/*
21513  * - LGPL
21514  *
21515  * Card header - holder for the card header elements.
21516  * 
21517  */
21518
21519 /**
21520  * @class Roo.bootstrap.PopoverNav
21521  * @extends Roo.bootstrap.NavGroup
21522  * Bootstrap Popover header navigation class
21523  * @constructor
21524  * Create a new Popover Header Navigation 
21525  * @param {Object} config The config object
21526  */
21527
21528 Roo.bootstrap.PopoverNav = function(config){
21529     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21530 };
21531
21532 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21533     
21534     
21535     container_method : 'getPopoverHeader' 
21536     
21537      
21538     
21539     
21540    
21541 });
21542
21543  
21544
21545  /*
21546  * - LGPL
21547  *
21548  * Progress
21549  * 
21550  */
21551
21552 /**
21553  * @class Roo.bootstrap.Progress
21554  * @extends Roo.bootstrap.Component
21555  * Bootstrap Progress class
21556  * @cfg {Boolean} striped striped of the progress bar
21557  * @cfg {Boolean} active animated of the progress bar
21558  * 
21559  * 
21560  * @constructor
21561  * Create a new Progress
21562  * @param {Object} config The config object
21563  */
21564
21565 Roo.bootstrap.Progress = function(config){
21566     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21567 };
21568
21569 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21570     
21571     striped : false,
21572     active: false,
21573     
21574     getAutoCreate : function(){
21575         var cfg = {
21576             tag: 'div',
21577             cls: 'progress'
21578         };
21579         
21580         
21581         if(this.striped){
21582             cfg.cls += ' progress-striped';
21583         }
21584       
21585         if(this.active){
21586             cfg.cls += ' active';
21587         }
21588         
21589         
21590         return cfg;
21591     }
21592    
21593 });
21594
21595  
21596
21597  /*
21598  * - LGPL
21599  *
21600  * ProgressBar
21601  * 
21602  */
21603
21604 /**
21605  * @class Roo.bootstrap.ProgressBar
21606  * @extends Roo.bootstrap.Component
21607  * Bootstrap ProgressBar class
21608  * @cfg {Number} aria_valuenow aria-value now
21609  * @cfg {Number} aria_valuemin aria-value min
21610  * @cfg {Number} aria_valuemax aria-value max
21611  * @cfg {String} label label for the progress bar
21612  * @cfg {String} panel (success | info | warning | danger )
21613  * @cfg {String} role role of the progress bar
21614  * @cfg {String} sr_only text
21615  * 
21616  * 
21617  * @constructor
21618  * Create a new ProgressBar
21619  * @param {Object} config The config object
21620  */
21621
21622 Roo.bootstrap.ProgressBar = function(config){
21623     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21624 };
21625
21626 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21627     
21628     aria_valuenow : 0,
21629     aria_valuemin : 0,
21630     aria_valuemax : 100,
21631     label : false,
21632     panel : false,
21633     role : false,
21634     sr_only: false,
21635     
21636     getAutoCreate : function()
21637     {
21638         
21639         var cfg = {
21640             tag: 'div',
21641             cls: 'progress-bar',
21642             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21643         };
21644         
21645         if(this.sr_only){
21646             cfg.cn = {
21647                 tag: 'span',
21648                 cls: 'sr-only',
21649                 html: this.sr_only
21650             }
21651         }
21652         
21653         if(this.role){
21654             cfg.role = this.role;
21655         }
21656         
21657         if(this.aria_valuenow){
21658             cfg['aria-valuenow'] = this.aria_valuenow;
21659         }
21660         
21661         if(this.aria_valuemin){
21662             cfg['aria-valuemin'] = this.aria_valuemin;
21663         }
21664         
21665         if(this.aria_valuemax){
21666             cfg['aria-valuemax'] = this.aria_valuemax;
21667         }
21668         
21669         if(this.label && !this.sr_only){
21670             cfg.html = this.label;
21671         }
21672         
21673         if(this.panel){
21674             cfg.cls += ' progress-bar-' + this.panel;
21675         }
21676         
21677         return cfg;
21678     },
21679     
21680     update : function(aria_valuenow)
21681     {
21682         this.aria_valuenow = aria_valuenow;
21683         
21684         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21685     }
21686    
21687 });
21688
21689  
21690
21691  /*
21692  * - LGPL
21693  *
21694  * column
21695  * 
21696  */
21697
21698 /**
21699  * @class Roo.bootstrap.TabGroup
21700  * @extends Roo.bootstrap.Column
21701  * Bootstrap Column class
21702  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21703  * @cfg {Boolean} carousel true to make the group behave like a carousel
21704  * @cfg {Boolean} bullets show bullets for the panels
21705  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21706  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21707  * @cfg {Boolean} showarrow (true|false) show arrow default true
21708  * 
21709  * @constructor
21710  * Create a new TabGroup
21711  * @param {Object} config The config object
21712  */
21713
21714 Roo.bootstrap.TabGroup = function(config){
21715     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21716     if (!this.navId) {
21717         this.navId = Roo.id();
21718     }
21719     this.tabs = [];
21720     Roo.bootstrap.TabGroup.register(this);
21721     
21722 };
21723
21724 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21725     
21726     carousel : false,
21727     transition : false,
21728     bullets : 0,
21729     timer : 0,
21730     autoslide : false,
21731     slideFn : false,
21732     slideOnTouch : false,
21733     showarrow : true,
21734     
21735     getAutoCreate : function()
21736     {
21737         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21738         
21739         cfg.cls += ' tab-content';
21740         
21741         if (this.carousel) {
21742             cfg.cls += ' carousel slide';
21743             
21744             cfg.cn = [{
21745                cls : 'carousel-inner',
21746                cn : []
21747             }];
21748         
21749             if(this.bullets  && !Roo.isTouch){
21750                 
21751                 var bullets = {
21752                     cls : 'carousel-bullets',
21753                     cn : []
21754                 };
21755                
21756                 if(this.bullets_cls){
21757                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21758                 }
21759                 
21760                 bullets.cn.push({
21761                     cls : 'clear'
21762                 });
21763                 
21764                 cfg.cn[0].cn.push(bullets);
21765             }
21766             
21767             if(this.showarrow){
21768                 cfg.cn[0].cn.push({
21769                     tag : 'div',
21770                     class : 'carousel-arrow',
21771                     cn : [
21772                         {
21773                             tag : 'div',
21774                             class : 'carousel-prev',
21775                             cn : [
21776                                 {
21777                                     tag : 'i',
21778                                     class : 'fa fa-chevron-left'
21779                                 }
21780                             ]
21781                         },
21782                         {
21783                             tag : 'div',
21784                             class : 'carousel-next',
21785                             cn : [
21786                                 {
21787                                     tag : 'i',
21788                                     class : 'fa fa-chevron-right'
21789                                 }
21790                             ]
21791                         }
21792                     ]
21793                 });
21794             }
21795             
21796         }
21797         
21798         return cfg;
21799     },
21800     
21801     initEvents:  function()
21802     {
21803 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21804 //            this.el.on("touchstart", this.onTouchStart, this);
21805 //        }
21806         
21807         if(this.autoslide){
21808             var _this = this;
21809             
21810             this.slideFn = window.setInterval(function() {
21811                 _this.showPanelNext();
21812             }, this.timer);
21813         }
21814         
21815         if(this.showarrow){
21816             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21817             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21818         }
21819         
21820         
21821     },
21822     
21823 //    onTouchStart : function(e, el, o)
21824 //    {
21825 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21826 //            return;
21827 //        }
21828 //        
21829 //        this.showPanelNext();
21830 //    },
21831     
21832     
21833     getChildContainer : function()
21834     {
21835         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21836     },
21837     
21838     /**
21839     * register a Navigation item
21840     * @param {Roo.bootstrap.NavItem} the navitem to add
21841     */
21842     register : function(item)
21843     {
21844         this.tabs.push( item);
21845         item.navId = this.navId; // not really needed..
21846         this.addBullet();
21847     
21848     },
21849     
21850     getActivePanel : function()
21851     {
21852         var r = false;
21853         Roo.each(this.tabs, function(t) {
21854             if (t.active) {
21855                 r = t;
21856                 return false;
21857             }
21858             return null;
21859         });
21860         return r;
21861         
21862     },
21863     getPanelByName : function(n)
21864     {
21865         var r = false;
21866         Roo.each(this.tabs, function(t) {
21867             if (t.tabId == n) {
21868                 r = t;
21869                 return false;
21870             }
21871             return null;
21872         });
21873         return r;
21874     },
21875     indexOfPanel : function(p)
21876     {
21877         var r = false;
21878         Roo.each(this.tabs, function(t,i) {
21879             if (t.tabId == p.tabId) {
21880                 r = i;
21881                 return false;
21882             }
21883             return null;
21884         });
21885         return r;
21886     },
21887     /**
21888      * show a specific panel
21889      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21890      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21891      */
21892     showPanel : function (pan)
21893     {
21894         if(this.transition || typeof(pan) == 'undefined'){
21895             Roo.log("waiting for the transitionend");
21896             return false;
21897         }
21898         
21899         if (typeof(pan) == 'number') {
21900             pan = this.tabs[pan];
21901         }
21902         
21903         if (typeof(pan) == 'string') {
21904             pan = this.getPanelByName(pan);
21905         }
21906         
21907         var cur = this.getActivePanel();
21908         
21909         if(!pan || !cur){
21910             Roo.log('pan or acitve pan is undefined');
21911             return false;
21912         }
21913         
21914         if (pan.tabId == this.getActivePanel().tabId) {
21915             return true;
21916         }
21917         
21918         if (false === cur.fireEvent('beforedeactivate')) {
21919             return false;
21920         }
21921         
21922         if(this.bullets > 0 && !Roo.isTouch){
21923             this.setActiveBullet(this.indexOfPanel(pan));
21924         }
21925         
21926         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21927             
21928             //class="carousel-item carousel-item-next carousel-item-left"
21929             
21930             this.transition = true;
21931             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21932             var lr = dir == 'next' ? 'left' : 'right';
21933             pan.el.addClass(dir); // or prev
21934             pan.el.addClass('carousel-item-' + dir); // or prev
21935             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21936             cur.el.addClass(lr); // or right
21937             pan.el.addClass(lr);
21938             cur.el.addClass('carousel-item-' +lr); // or right
21939             pan.el.addClass('carousel-item-' +lr);
21940             
21941             
21942             var _this = this;
21943             cur.el.on('transitionend', function() {
21944                 Roo.log("trans end?");
21945                 
21946                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21947                 pan.setActive(true);
21948                 
21949                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21950                 cur.setActive(false);
21951                 
21952                 _this.transition = false;
21953                 
21954             }, this, { single:  true } );
21955             
21956             return true;
21957         }
21958         
21959         cur.setActive(false);
21960         pan.setActive(true);
21961         
21962         return true;
21963         
21964     },
21965     showPanelNext : function()
21966     {
21967         var i = this.indexOfPanel(this.getActivePanel());
21968         
21969         if (i >= this.tabs.length - 1 && !this.autoslide) {
21970             return;
21971         }
21972         
21973         if (i >= this.tabs.length - 1 && this.autoslide) {
21974             i = -1;
21975         }
21976         
21977         this.showPanel(this.tabs[i+1]);
21978     },
21979     
21980     showPanelPrev : function()
21981     {
21982         var i = this.indexOfPanel(this.getActivePanel());
21983         
21984         if (i  < 1 && !this.autoslide) {
21985             return;
21986         }
21987         
21988         if (i < 1 && this.autoslide) {
21989             i = this.tabs.length;
21990         }
21991         
21992         this.showPanel(this.tabs[i-1]);
21993     },
21994     
21995     
21996     addBullet: function()
21997     {
21998         if(!this.bullets || Roo.isTouch){
21999             return;
22000         }
22001         var ctr = this.el.select('.carousel-bullets',true).first();
22002         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22003         var bullet = ctr.createChild({
22004             cls : 'bullet bullet-' + i
22005         },ctr.dom.lastChild);
22006         
22007         
22008         var _this = this;
22009         
22010         bullet.on('click', (function(e, el, o, ii, t){
22011
22012             e.preventDefault();
22013
22014             this.showPanel(ii);
22015
22016             if(this.autoslide && this.slideFn){
22017                 clearInterval(this.slideFn);
22018                 this.slideFn = window.setInterval(function() {
22019                     _this.showPanelNext();
22020                 }, this.timer);
22021             }
22022
22023         }).createDelegate(this, [i, bullet], true));
22024                 
22025         
22026     },
22027      
22028     setActiveBullet : function(i)
22029     {
22030         if(Roo.isTouch){
22031             return;
22032         }
22033         
22034         Roo.each(this.el.select('.bullet', true).elements, function(el){
22035             el.removeClass('selected');
22036         });
22037
22038         var bullet = this.el.select('.bullet-' + i, true).first();
22039         
22040         if(!bullet){
22041             return;
22042         }
22043         
22044         bullet.addClass('selected');
22045     }
22046     
22047     
22048   
22049 });
22050
22051  
22052
22053  
22054  
22055 Roo.apply(Roo.bootstrap.TabGroup, {
22056     
22057     groups: {},
22058      /**
22059     * register a Navigation Group
22060     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22061     */
22062     register : function(navgrp)
22063     {
22064         this.groups[navgrp.navId] = navgrp;
22065         
22066     },
22067     /**
22068     * fetch a Navigation Group based on the navigation ID
22069     * if one does not exist , it will get created.
22070     * @param {string} the navgroup to add
22071     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22072     */
22073     get: function(navId) {
22074         if (typeof(this.groups[navId]) == 'undefined') {
22075             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22076         }
22077         return this.groups[navId] ;
22078     }
22079     
22080     
22081     
22082 });
22083
22084  /*
22085  * - LGPL
22086  *
22087  * TabPanel
22088  * 
22089  */
22090
22091 /**
22092  * @class Roo.bootstrap.TabPanel
22093  * @extends Roo.bootstrap.Component
22094  * Bootstrap TabPanel class
22095  * @cfg {Boolean} active panel active
22096  * @cfg {String} html panel content
22097  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22098  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22099  * @cfg {String} href click to link..
22100  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22101  * 
22102  * 
22103  * @constructor
22104  * Create a new TabPanel
22105  * @param {Object} config The config object
22106  */
22107
22108 Roo.bootstrap.TabPanel = function(config){
22109     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22110     this.addEvents({
22111         /**
22112              * @event changed
22113              * Fires when the active status changes
22114              * @param {Roo.bootstrap.TabPanel} this
22115              * @param {Boolean} state the new state
22116             
22117          */
22118         'changed': true,
22119         /**
22120              * @event beforedeactivate
22121              * Fires before a tab is de-activated - can be used to do validation on a form.
22122              * @param {Roo.bootstrap.TabPanel} this
22123              * @return {Boolean} false if there is an error
22124             
22125          */
22126         'beforedeactivate': true
22127      });
22128     
22129     this.tabId = this.tabId || Roo.id();
22130   
22131 };
22132
22133 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22134     
22135     active: false,
22136     html: false,
22137     tabId: false,
22138     navId : false,
22139     href : '',
22140     touchSlide : false,
22141     getAutoCreate : function(){
22142         
22143         
22144         var cfg = {
22145             tag: 'div',
22146             // item is needed for carousel - not sure if it has any effect otherwise
22147             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22148             html: this.html || ''
22149         };
22150         
22151         if(this.active){
22152             cfg.cls += ' active';
22153         }
22154         
22155         if(this.tabId){
22156             cfg.tabId = this.tabId;
22157         }
22158         
22159         
22160         
22161         return cfg;
22162     },
22163     
22164     initEvents:  function()
22165     {
22166         var p = this.parent();
22167         
22168         this.navId = this.navId || p.navId;
22169         
22170         if (typeof(this.navId) != 'undefined') {
22171             // not really needed.. but just in case.. parent should be a NavGroup.
22172             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22173             
22174             tg.register(this);
22175             
22176             var i = tg.tabs.length - 1;
22177             
22178             if(this.active && tg.bullets > 0 && i < tg.bullets){
22179                 tg.setActiveBullet(i);
22180             }
22181         }
22182         
22183         this.el.on('click', this.onClick, this);
22184         
22185         if(Roo.isTouch && this.touchSlide){
22186             this.el.on("touchstart", this.onTouchStart, this);
22187             this.el.on("touchmove", this.onTouchMove, this);
22188             this.el.on("touchend", this.onTouchEnd, this);
22189         }
22190         
22191     },
22192     
22193     onRender : function(ct, position)
22194     {
22195         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22196     },
22197     
22198     setActive : function(state)
22199     {
22200         Roo.log("panel - set active " + this.tabId + "=" + state);
22201         
22202         this.active = state;
22203         if (!state) {
22204             this.el.removeClass('active');
22205             
22206         } else  if (!this.el.hasClass('active')) {
22207             this.el.addClass('active');
22208         }
22209         
22210         this.fireEvent('changed', this, state);
22211     },
22212     
22213     onClick : function(e)
22214     {
22215         e.preventDefault();
22216         
22217         if(!this.href.length){
22218             return;
22219         }
22220         
22221         window.location.href = this.href;
22222     },
22223     
22224     startX : 0,
22225     startY : 0,
22226     endX : 0,
22227     endY : 0,
22228     swiping : false,
22229     
22230     onTouchStart : function(e)
22231     {
22232         this.swiping = false;
22233         
22234         this.startX = e.browserEvent.touches[0].clientX;
22235         this.startY = e.browserEvent.touches[0].clientY;
22236     },
22237     
22238     onTouchMove : function(e)
22239     {
22240         this.swiping = true;
22241         
22242         this.endX = e.browserEvent.touches[0].clientX;
22243         this.endY = e.browserEvent.touches[0].clientY;
22244     },
22245     
22246     onTouchEnd : function(e)
22247     {
22248         if(!this.swiping){
22249             this.onClick(e);
22250             return;
22251         }
22252         
22253         var tabGroup = this.parent();
22254         
22255         if(this.endX > this.startX){ // swiping right
22256             tabGroup.showPanelPrev();
22257             return;
22258         }
22259         
22260         if(this.startX > this.endX){ // swiping left
22261             tabGroup.showPanelNext();
22262             return;
22263         }
22264     }
22265     
22266     
22267 });
22268  
22269
22270  
22271
22272  /*
22273  * - LGPL
22274  *
22275  * DateField
22276  * 
22277  */
22278
22279 /**
22280  * @class Roo.bootstrap.DateField
22281  * @extends Roo.bootstrap.Input
22282  * Bootstrap DateField class
22283  * @cfg {Number} weekStart default 0
22284  * @cfg {String} viewMode default empty, (months|years)
22285  * @cfg {String} minViewMode default empty, (months|years)
22286  * @cfg {Number} startDate default -Infinity
22287  * @cfg {Number} endDate default Infinity
22288  * @cfg {Boolean} todayHighlight default false
22289  * @cfg {Boolean} todayBtn default false
22290  * @cfg {Boolean} calendarWeeks default false
22291  * @cfg {Object} daysOfWeekDisabled default empty
22292  * @cfg {Boolean} singleMode default false (true | false)
22293  * 
22294  * @cfg {Boolean} keyboardNavigation default true
22295  * @cfg {String} language default en
22296  * 
22297  * @constructor
22298  * Create a new DateField
22299  * @param {Object} config The config object
22300  */
22301
22302 Roo.bootstrap.DateField = function(config){
22303     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22304      this.addEvents({
22305             /**
22306              * @event show
22307              * Fires when this field show.
22308              * @param {Roo.bootstrap.DateField} this
22309              * @param {Mixed} date The date value
22310              */
22311             show : true,
22312             /**
22313              * @event show
22314              * Fires when this field hide.
22315              * @param {Roo.bootstrap.DateField} this
22316              * @param {Mixed} date The date value
22317              */
22318             hide : true,
22319             /**
22320              * @event select
22321              * Fires when select a date.
22322              * @param {Roo.bootstrap.DateField} this
22323              * @param {Mixed} date The date value
22324              */
22325             select : true,
22326             /**
22327              * @event beforeselect
22328              * Fires when before select a date.
22329              * @param {Roo.bootstrap.DateField} this
22330              * @param {Mixed} date The date value
22331              */
22332             beforeselect : true
22333         });
22334 };
22335
22336 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22337     
22338     /**
22339      * @cfg {String} format
22340      * The default date format string which can be overriden for localization support.  The format must be
22341      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22342      */
22343     format : "m/d/y",
22344     /**
22345      * @cfg {String} altFormats
22346      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22347      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22348      */
22349     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22350     
22351     weekStart : 0,
22352     
22353     viewMode : '',
22354     
22355     minViewMode : '',
22356     
22357     todayHighlight : false,
22358     
22359     todayBtn: false,
22360     
22361     language: 'en',
22362     
22363     keyboardNavigation: true,
22364     
22365     calendarWeeks: false,
22366     
22367     startDate: -Infinity,
22368     
22369     endDate: Infinity,
22370     
22371     daysOfWeekDisabled: [],
22372     
22373     _events: [],
22374     
22375     singleMode : false,
22376     
22377     UTCDate: function()
22378     {
22379         return new Date(Date.UTC.apply(Date, arguments));
22380     },
22381     
22382     UTCToday: function()
22383     {
22384         var today = new Date();
22385         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22386     },
22387     
22388     getDate: function() {
22389             var d = this.getUTCDate();
22390             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22391     },
22392     
22393     getUTCDate: function() {
22394             return this.date;
22395     },
22396     
22397     setDate: function(d) {
22398             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22399     },
22400     
22401     setUTCDate: function(d) {
22402             this.date = d;
22403             this.setValue(this.formatDate(this.date));
22404     },
22405         
22406     onRender: function(ct, position)
22407     {
22408         
22409         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22410         
22411         this.language = this.language || 'en';
22412         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22413         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22414         
22415         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22416         this.format = this.format || 'm/d/y';
22417         this.isInline = false;
22418         this.isInput = true;
22419         this.component = this.el.select('.add-on', true).first() || false;
22420         this.component = (this.component && this.component.length === 0) ? false : this.component;
22421         this.hasInput = this.component && this.inputEl().length;
22422         
22423         if (typeof(this.minViewMode === 'string')) {
22424             switch (this.minViewMode) {
22425                 case 'months':
22426                     this.minViewMode = 1;
22427                     break;
22428                 case 'years':
22429                     this.minViewMode = 2;
22430                     break;
22431                 default:
22432                     this.minViewMode = 0;
22433                     break;
22434             }
22435         }
22436         
22437         if (typeof(this.viewMode === 'string')) {
22438             switch (this.viewMode) {
22439                 case 'months':
22440                     this.viewMode = 1;
22441                     break;
22442                 case 'years':
22443                     this.viewMode = 2;
22444                     break;
22445                 default:
22446                     this.viewMode = 0;
22447                     break;
22448             }
22449         }
22450                 
22451         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22452         
22453 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22454         
22455         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22456         
22457         this.picker().on('mousedown', this.onMousedown, this);
22458         this.picker().on('click', this.onClick, this);
22459         
22460         this.picker().addClass('datepicker-dropdown');
22461         
22462         this.startViewMode = this.viewMode;
22463         
22464         if(this.singleMode){
22465             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22466                 v.setVisibilityMode(Roo.Element.DISPLAY);
22467                 v.hide();
22468             });
22469             
22470             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22471                 v.setStyle('width', '189px');
22472             });
22473         }
22474         
22475         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22476             if(!this.calendarWeeks){
22477                 v.remove();
22478                 return;
22479             }
22480             
22481             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22482             v.attr('colspan', function(i, val){
22483                 return parseInt(val) + 1;
22484             });
22485         });
22486                         
22487         
22488         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22489         
22490         this.setStartDate(this.startDate);
22491         this.setEndDate(this.endDate);
22492         
22493         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22494         
22495         this.fillDow();
22496         this.fillMonths();
22497         this.update();
22498         this.showMode();
22499         
22500         if(this.isInline) {
22501             this.showPopup();
22502         }
22503     },
22504     
22505     picker : function()
22506     {
22507         return this.pickerEl;
22508 //        return this.el.select('.datepicker', true).first();
22509     },
22510     
22511     fillDow: function()
22512     {
22513         var dowCnt = this.weekStart;
22514         
22515         var dow = {
22516             tag: 'tr',
22517             cn: [
22518                 
22519             ]
22520         };
22521         
22522         if(this.calendarWeeks){
22523             dow.cn.push({
22524                 tag: 'th',
22525                 cls: 'cw',
22526                 html: '&nbsp;'
22527             })
22528         }
22529         
22530         while (dowCnt < this.weekStart + 7) {
22531             dow.cn.push({
22532                 tag: 'th',
22533                 cls: 'dow',
22534                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22535             });
22536         }
22537         
22538         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22539     },
22540     
22541     fillMonths: function()
22542     {    
22543         var i = 0;
22544         var months = this.picker().select('>.datepicker-months td', true).first();
22545         
22546         months.dom.innerHTML = '';
22547         
22548         while (i < 12) {
22549             var month = {
22550                 tag: 'span',
22551                 cls: 'month',
22552                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22553             };
22554             
22555             months.createChild(month);
22556         }
22557         
22558     },
22559     
22560     update: function()
22561     {
22562         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;
22563         
22564         if (this.date < this.startDate) {
22565             this.viewDate = new Date(this.startDate);
22566         } else if (this.date > this.endDate) {
22567             this.viewDate = new Date(this.endDate);
22568         } else {
22569             this.viewDate = new Date(this.date);
22570         }
22571         
22572         this.fill();
22573     },
22574     
22575     fill: function() 
22576     {
22577         var d = new Date(this.viewDate),
22578                 year = d.getUTCFullYear(),
22579                 month = d.getUTCMonth(),
22580                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22581                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22582                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22583                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22584                 currentDate = this.date && this.date.valueOf(),
22585                 today = this.UTCToday();
22586         
22587         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22588         
22589 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22590         
22591 //        this.picker.select('>tfoot th.today').
22592 //                                              .text(dates[this.language].today)
22593 //                                              .toggle(this.todayBtn !== false);
22594     
22595         this.updateNavArrows();
22596         this.fillMonths();
22597                                                 
22598         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22599         
22600         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22601          
22602         prevMonth.setUTCDate(day);
22603         
22604         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22605         
22606         var nextMonth = new Date(prevMonth);
22607         
22608         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22609         
22610         nextMonth = nextMonth.valueOf();
22611         
22612         var fillMonths = false;
22613         
22614         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22615         
22616         while(prevMonth.valueOf() <= nextMonth) {
22617             var clsName = '';
22618             
22619             if (prevMonth.getUTCDay() === this.weekStart) {
22620                 if(fillMonths){
22621                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22622                 }
22623                     
22624                 fillMonths = {
22625                     tag: 'tr',
22626                     cn: []
22627                 };
22628                 
22629                 if(this.calendarWeeks){
22630                     // ISO 8601: First week contains first thursday.
22631                     // ISO also states week starts on Monday, but we can be more abstract here.
22632                     var
22633                     // Start of current week: based on weekstart/current date
22634                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22635                     // Thursday of this week
22636                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22637                     // First Thursday of year, year from thursday
22638                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22639                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22640                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22641                     
22642                     fillMonths.cn.push({
22643                         tag: 'td',
22644                         cls: 'cw',
22645                         html: calWeek
22646                     });
22647                 }
22648             }
22649             
22650             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22651                 clsName += ' old';
22652             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22653                 clsName += ' new';
22654             }
22655             if (this.todayHighlight &&
22656                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22657                 prevMonth.getUTCMonth() == today.getMonth() &&
22658                 prevMonth.getUTCDate() == today.getDate()) {
22659                 clsName += ' today';
22660             }
22661             
22662             if (currentDate && prevMonth.valueOf() === currentDate) {
22663                 clsName += ' active';
22664             }
22665             
22666             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22667                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22668                     clsName += ' disabled';
22669             }
22670             
22671             fillMonths.cn.push({
22672                 tag: 'td',
22673                 cls: 'day ' + clsName,
22674                 html: prevMonth.getDate()
22675             });
22676             
22677             prevMonth.setDate(prevMonth.getDate()+1);
22678         }
22679           
22680         var currentYear = this.date && this.date.getUTCFullYear();
22681         var currentMonth = this.date && this.date.getUTCMonth();
22682         
22683         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22684         
22685         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22686             v.removeClass('active');
22687             
22688             if(currentYear === year && k === currentMonth){
22689                 v.addClass('active');
22690             }
22691             
22692             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22693                 v.addClass('disabled');
22694             }
22695             
22696         });
22697         
22698         
22699         year = parseInt(year/10, 10) * 10;
22700         
22701         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22702         
22703         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22704         
22705         year -= 1;
22706         for (var i = -1; i < 11; i++) {
22707             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22708                 tag: 'span',
22709                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22710                 html: year
22711             });
22712             
22713             year += 1;
22714         }
22715     },
22716     
22717     showMode: function(dir) 
22718     {
22719         if (dir) {
22720             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22721         }
22722         
22723         Roo.each(this.picker().select('>div',true).elements, function(v){
22724             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22725             v.hide();
22726         });
22727         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22728     },
22729     
22730     place: function()
22731     {
22732         if(this.isInline) {
22733             return;
22734         }
22735         
22736         this.picker().removeClass(['bottom', 'top']);
22737         
22738         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22739             /*
22740              * place to the top of element!
22741              *
22742              */
22743             
22744             this.picker().addClass('top');
22745             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22746             
22747             return;
22748         }
22749         
22750         this.picker().addClass('bottom');
22751         
22752         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22753     },
22754     
22755     parseDate : function(value)
22756     {
22757         if(!value || value instanceof Date){
22758             return value;
22759         }
22760         var v = Date.parseDate(value, this.format);
22761         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22762             v = Date.parseDate(value, 'Y-m-d');
22763         }
22764         if(!v && this.altFormats){
22765             if(!this.altFormatsArray){
22766                 this.altFormatsArray = this.altFormats.split("|");
22767             }
22768             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22769                 v = Date.parseDate(value, this.altFormatsArray[i]);
22770             }
22771         }
22772         return v;
22773     },
22774     
22775     formatDate : function(date, fmt)
22776     {   
22777         return (!date || !(date instanceof Date)) ?
22778         date : date.dateFormat(fmt || this.format);
22779     },
22780     
22781     onFocus : function()
22782     {
22783         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22784         this.showPopup();
22785     },
22786     
22787     onBlur : function()
22788     {
22789         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22790         
22791         var d = this.inputEl().getValue();
22792         
22793         this.setValue(d);
22794                 
22795         this.hidePopup();
22796     },
22797     
22798     showPopup : function()
22799     {
22800         this.picker().show();
22801         this.update();
22802         this.place();
22803         
22804         this.fireEvent('showpopup', this, this.date);
22805     },
22806     
22807     hidePopup : function()
22808     {
22809         if(this.isInline) {
22810             return;
22811         }
22812         this.picker().hide();
22813         this.viewMode = this.startViewMode;
22814         this.showMode();
22815         
22816         this.fireEvent('hidepopup', this, this.date);
22817         
22818     },
22819     
22820     onMousedown: function(e)
22821     {
22822         e.stopPropagation();
22823         e.preventDefault();
22824     },
22825     
22826     keyup: function(e)
22827     {
22828         Roo.bootstrap.DateField.superclass.keyup.call(this);
22829         this.update();
22830     },
22831
22832     setValue: function(v)
22833     {
22834         if(this.fireEvent('beforeselect', this, v) !== false){
22835             var d = new Date(this.parseDate(v) ).clearTime();
22836         
22837             if(isNaN(d.getTime())){
22838                 this.date = this.viewDate = '';
22839                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22840                 return;
22841             }
22842
22843             v = this.formatDate(d);
22844
22845             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22846
22847             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22848
22849             this.update();
22850
22851             this.fireEvent('select', this, this.date);
22852         }
22853     },
22854     
22855     getValue: function()
22856     {
22857         return this.formatDate(this.date);
22858     },
22859     
22860     fireKey: function(e)
22861     {
22862         if (!this.picker().isVisible()){
22863             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22864                 this.showPopup();
22865             }
22866             return;
22867         }
22868         
22869         var dateChanged = false,
22870         dir, day, month,
22871         newDate, newViewDate;
22872         
22873         switch(e.keyCode){
22874             case 27: // escape
22875                 this.hidePopup();
22876                 e.preventDefault();
22877                 break;
22878             case 37: // left
22879             case 39: // right
22880                 if (!this.keyboardNavigation) {
22881                     break;
22882                 }
22883                 dir = e.keyCode == 37 ? -1 : 1;
22884                 
22885                 if (e.ctrlKey){
22886                     newDate = this.moveYear(this.date, dir);
22887                     newViewDate = this.moveYear(this.viewDate, dir);
22888                 } else if (e.shiftKey){
22889                     newDate = this.moveMonth(this.date, dir);
22890                     newViewDate = this.moveMonth(this.viewDate, dir);
22891                 } else {
22892                     newDate = new Date(this.date);
22893                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22894                     newViewDate = new Date(this.viewDate);
22895                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22896                 }
22897                 if (this.dateWithinRange(newDate)){
22898                     this.date = newDate;
22899                     this.viewDate = newViewDate;
22900                     this.setValue(this.formatDate(this.date));
22901 //                    this.update();
22902                     e.preventDefault();
22903                     dateChanged = true;
22904                 }
22905                 break;
22906             case 38: // up
22907             case 40: // down
22908                 if (!this.keyboardNavigation) {
22909                     break;
22910                 }
22911                 dir = e.keyCode == 38 ? -1 : 1;
22912                 if (e.ctrlKey){
22913                     newDate = this.moveYear(this.date, dir);
22914                     newViewDate = this.moveYear(this.viewDate, dir);
22915                 } else if (e.shiftKey){
22916                     newDate = this.moveMonth(this.date, dir);
22917                     newViewDate = this.moveMonth(this.viewDate, dir);
22918                 } else {
22919                     newDate = new Date(this.date);
22920                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22921                     newViewDate = new Date(this.viewDate);
22922                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22923                 }
22924                 if (this.dateWithinRange(newDate)){
22925                     this.date = newDate;
22926                     this.viewDate = newViewDate;
22927                     this.setValue(this.formatDate(this.date));
22928 //                    this.update();
22929                     e.preventDefault();
22930                     dateChanged = true;
22931                 }
22932                 break;
22933             case 13: // enter
22934                 this.setValue(this.formatDate(this.date));
22935                 this.hidePopup();
22936                 e.preventDefault();
22937                 break;
22938             case 9: // tab
22939                 this.setValue(this.formatDate(this.date));
22940                 this.hidePopup();
22941                 break;
22942             case 16: // shift
22943             case 17: // ctrl
22944             case 18: // alt
22945                 break;
22946             default :
22947                 this.hidePopup();
22948                 
22949         }
22950     },
22951     
22952     
22953     onClick: function(e) 
22954     {
22955         e.stopPropagation();
22956         e.preventDefault();
22957         
22958         var target = e.getTarget();
22959         
22960         if(target.nodeName.toLowerCase() === 'i'){
22961             target = Roo.get(target).dom.parentNode;
22962         }
22963         
22964         var nodeName = target.nodeName;
22965         var className = target.className;
22966         var html = target.innerHTML;
22967         //Roo.log(nodeName);
22968         
22969         switch(nodeName.toLowerCase()) {
22970             case 'th':
22971                 switch(className) {
22972                     case 'switch':
22973                         this.showMode(1);
22974                         break;
22975                     case 'prev':
22976                     case 'next':
22977                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22978                         switch(this.viewMode){
22979                                 case 0:
22980                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22981                                         break;
22982                                 case 1:
22983                                 case 2:
22984                                         this.viewDate = this.moveYear(this.viewDate, dir);
22985                                         break;
22986                         }
22987                         this.fill();
22988                         break;
22989                     case 'today':
22990                         var date = new Date();
22991                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22992 //                        this.fill()
22993                         this.setValue(this.formatDate(this.date));
22994                         
22995                         this.hidePopup();
22996                         break;
22997                 }
22998                 break;
22999             case 'span':
23000                 if (className.indexOf('disabled') < 0) {
23001                 if (!this.viewDate) {
23002                     this.viewDate = new Date();
23003                 }
23004                 this.viewDate.setUTCDate(1);
23005                     if (className.indexOf('month') > -1) {
23006                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23007                     } else {
23008                         var year = parseInt(html, 10) || 0;
23009                         this.viewDate.setUTCFullYear(year);
23010                         
23011                     }
23012                     
23013                     if(this.singleMode){
23014                         this.setValue(this.formatDate(this.viewDate));
23015                         this.hidePopup();
23016                         return;
23017                     }
23018                     
23019                     this.showMode(-1);
23020                     this.fill();
23021                 }
23022                 break;
23023                 
23024             case 'td':
23025                 //Roo.log(className);
23026                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23027                     var day = parseInt(html, 10) || 1;
23028                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23029                         month = (this.viewDate || new Date()).getUTCMonth();
23030
23031                     if (className.indexOf('old') > -1) {
23032                         if(month === 0 ){
23033                             month = 11;
23034                             year -= 1;
23035                         }else{
23036                             month -= 1;
23037                         }
23038                     } else if (className.indexOf('new') > -1) {
23039                         if (month == 11) {
23040                             month = 0;
23041                             year += 1;
23042                         } else {
23043                             month += 1;
23044                         }
23045                     }
23046                     //Roo.log([year,month,day]);
23047                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23048                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23049 //                    this.fill();
23050                     //Roo.log(this.formatDate(this.date));
23051                     this.setValue(this.formatDate(this.date));
23052                     this.hidePopup();
23053                 }
23054                 break;
23055         }
23056     },
23057     
23058     setStartDate: function(startDate)
23059     {
23060         this.startDate = startDate || -Infinity;
23061         if (this.startDate !== -Infinity) {
23062             this.startDate = this.parseDate(this.startDate);
23063         }
23064         this.update();
23065         this.updateNavArrows();
23066     },
23067
23068     setEndDate: function(endDate)
23069     {
23070         this.endDate = endDate || Infinity;
23071         if (this.endDate !== Infinity) {
23072             this.endDate = this.parseDate(this.endDate);
23073         }
23074         this.update();
23075         this.updateNavArrows();
23076     },
23077     
23078     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23079     {
23080         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23081         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23082             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23083         }
23084         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23085             return parseInt(d, 10);
23086         });
23087         this.update();
23088         this.updateNavArrows();
23089     },
23090     
23091     updateNavArrows: function() 
23092     {
23093         if(this.singleMode){
23094             return;
23095         }
23096         
23097         var d = new Date(this.viewDate),
23098         year = d.getUTCFullYear(),
23099         month = d.getUTCMonth();
23100         
23101         Roo.each(this.picker().select('.prev', true).elements, function(v){
23102             v.show();
23103             switch (this.viewMode) {
23104                 case 0:
23105
23106                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23107                         v.hide();
23108                     }
23109                     break;
23110                 case 1:
23111                 case 2:
23112                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23113                         v.hide();
23114                     }
23115                     break;
23116             }
23117         });
23118         
23119         Roo.each(this.picker().select('.next', true).elements, function(v){
23120             v.show();
23121             switch (this.viewMode) {
23122                 case 0:
23123
23124                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23125                         v.hide();
23126                     }
23127                     break;
23128                 case 1:
23129                 case 2:
23130                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23131                         v.hide();
23132                     }
23133                     break;
23134             }
23135         })
23136     },
23137     
23138     moveMonth: function(date, dir)
23139     {
23140         if (!dir) {
23141             return date;
23142         }
23143         var new_date = new Date(date.valueOf()),
23144         day = new_date.getUTCDate(),
23145         month = new_date.getUTCMonth(),
23146         mag = Math.abs(dir),
23147         new_month, test;
23148         dir = dir > 0 ? 1 : -1;
23149         if (mag == 1){
23150             test = dir == -1
23151             // If going back one month, make sure month is not current month
23152             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23153             ? function(){
23154                 return new_date.getUTCMonth() == month;
23155             }
23156             // If going forward one month, make sure month is as expected
23157             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23158             : function(){
23159                 return new_date.getUTCMonth() != new_month;
23160             };
23161             new_month = month + dir;
23162             new_date.setUTCMonth(new_month);
23163             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23164             if (new_month < 0 || new_month > 11) {
23165                 new_month = (new_month + 12) % 12;
23166             }
23167         } else {
23168             // For magnitudes >1, move one month at a time...
23169             for (var i=0; i<mag; i++) {
23170                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23171                 new_date = this.moveMonth(new_date, dir);
23172             }
23173             // ...then reset the day, keeping it in the new month
23174             new_month = new_date.getUTCMonth();
23175             new_date.setUTCDate(day);
23176             test = function(){
23177                 return new_month != new_date.getUTCMonth();
23178             };
23179         }
23180         // Common date-resetting loop -- if date is beyond end of month, make it
23181         // end of month
23182         while (test()){
23183             new_date.setUTCDate(--day);
23184             new_date.setUTCMonth(new_month);
23185         }
23186         return new_date;
23187     },
23188
23189     moveYear: function(date, dir)
23190     {
23191         return this.moveMonth(date, dir*12);
23192     },
23193
23194     dateWithinRange: function(date)
23195     {
23196         return date >= this.startDate && date <= this.endDate;
23197     },
23198
23199     
23200     remove: function() 
23201     {
23202         this.picker().remove();
23203     },
23204     
23205     validateValue : function(value)
23206     {
23207         if(this.getVisibilityEl().hasClass('hidden')){
23208             return true;
23209         }
23210         
23211         if(value.length < 1)  {
23212             if(this.allowBlank){
23213                 return true;
23214             }
23215             return false;
23216         }
23217         
23218         if(value.length < this.minLength){
23219             return false;
23220         }
23221         if(value.length > this.maxLength){
23222             return false;
23223         }
23224         if(this.vtype){
23225             var vt = Roo.form.VTypes;
23226             if(!vt[this.vtype](value, this)){
23227                 return false;
23228             }
23229         }
23230         if(typeof this.validator == "function"){
23231             var msg = this.validator(value);
23232             if(msg !== true){
23233                 return false;
23234             }
23235         }
23236         
23237         if(this.regex && !this.regex.test(value)){
23238             return false;
23239         }
23240         
23241         if(typeof(this.parseDate(value)) == 'undefined'){
23242             return false;
23243         }
23244         
23245         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23246             return false;
23247         }      
23248         
23249         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23250             return false;
23251         } 
23252         
23253         
23254         return true;
23255     },
23256     
23257     reset : function()
23258     {
23259         this.date = this.viewDate = '';
23260         
23261         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23262     }
23263    
23264 });
23265
23266 Roo.apply(Roo.bootstrap.DateField,  {
23267     
23268     head : {
23269         tag: 'thead',
23270         cn: [
23271         {
23272             tag: 'tr',
23273             cn: [
23274             {
23275                 tag: 'th',
23276                 cls: 'prev',
23277                 html: '<i class="fa fa-arrow-left"/>'
23278             },
23279             {
23280                 tag: 'th',
23281                 cls: 'switch',
23282                 colspan: '5'
23283             },
23284             {
23285                 tag: 'th',
23286                 cls: 'next',
23287                 html: '<i class="fa fa-arrow-right"/>'
23288             }
23289
23290             ]
23291         }
23292         ]
23293     },
23294     
23295     content : {
23296         tag: 'tbody',
23297         cn: [
23298         {
23299             tag: 'tr',
23300             cn: [
23301             {
23302                 tag: 'td',
23303                 colspan: '7'
23304             }
23305             ]
23306         }
23307         ]
23308     },
23309     
23310     footer : {
23311         tag: 'tfoot',
23312         cn: [
23313         {
23314             tag: 'tr',
23315             cn: [
23316             {
23317                 tag: 'th',
23318                 colspan: '7',
23319                 cls: 'today'
23320             }
23321                     
23322             ]
23323         }
23324         ]
23325     },
23326     
23327     dates:{
23328         en: {
23329             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23330             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23331             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23332             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23333             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23334             today: "Today"
23335         }
23336     },
23337     
23338     modes: [
23339     {
23340         clsName: 'days',
23341         navFnc: 'Month',
23342         navStep: 1
23343     },
23344     {
23345         clsName: 'months',
23346         navFnc: 'FullYear',
23347         navStep: 1
23348     },
23349     {
23350         clsName: 'years',
23351         navFnc: 'FullYear',
23352         navStep: 10
23353     }]
23354 });
23355
23356 Roo.apply(Roo.bootstrap.DateField,  {
23357   
23358     template : {
23359         tag: 'div',
23360         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23361         cn: [
23362         {
23363             tag: 'div',
23364             cls: 'datepicker-days',
23365             cn: [
23366             {
23367                 tag: 'table',
23368                 cls: 'table-condensed',
23369                 cn:[
23370                 Roo.bootstrap.DateField.head,
23371                 {
23372                     tag: 'tbody'
23373                 },
23374                 Roo.bootstrap.DateField.footer
23375                 ]
23376             }
23377             ]
23378         },
23379         {
23380             tag: 'div',
23381             cls: 'datepicker-months',
23382             cn: [
23383             {
23384                 tag: 'table',
23385                 cls: 'table-condensed',
23386                 cn:[
23387                 Roo.bootstrap.DateField.head,
23388                 Roo.bootstrap.DateField.content,
23389                 Roo.bootstrap.DateField.footer
23390                 ]
23391             }
23392             ]
23393         },
23394         {
23395             tag: 'div',
23396             cls: 'datepicker-years',
23397             cn: [
23398             {
23399                 tag: 'table',
23400                 cls: 'table-condensed',
23401                 cn:[
23402                 Roo.bootstrap.DateField.head,
23403                 Roo.bootstrap.DateField.content,
23404                 Roo.bootstrap.DateField.footer
23405                 ]
23406             }
23407             ]
23408         }
23409         ]
23410     }
23411 });
23412
23413  
23414
23415  /*
23416  * - LGPL
23417  *
23418  * TimeField
23419  * 
23420  */
23421
23422 /**
23423  * @class Roo.bootstrap.TimeField
23424  * @extends Roo.bootstrap.Input
23425  * Bootstrap DateField class
23426  * 
23427  * 
23428  * @constructor
23429  * Create a new TimeField
23430  * @param {Object} config The config object
23431  */
23432
23433 Roo.bootstrap.TimeField = function(config){
23434     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23435     this.addEvents({
23436             /**
23437              * @event show
23438              * Fires when this field show.
23439              * @param {Roo.bootstrap.DateField} thisthis
23440              * @param {Mixed} date The date value
23441              */
23442             show : true,
23443             /**
23444              * @event show
23445              * Fires when this field hide.
23446              * @param {Roo.bootstrap.DateField} this
23447              * @param {Mixed} date The date value
23448              */
23449             hide : true,
23450             /**
23451              * @event select
23452              * Fires when select a date.
23453              * @param {Roo.bootstrap.DateField} this
23454              * @param {Mixed} date The date value
23455              */
23456             select : true
23457         });
23458 };
23459
23460 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23461     
23462     /**
23463      * @cfg {String} format
23464      * The default time format string which can be overriden for localization support.  The format must be
23465      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23466      */
23467     format : "H:i",
23468
23469     getAutoCreate : function()
23470     {
23471         this.after = '<i class="fa far fa-clock"></i>';
23472         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23473         
23474          
23475     },
23476     onRender: function(ct, position)
23477     {
23478         
23479         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23480                 
23481         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23482         
23483         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23484         
23485         this.pop = this.picker().select('>.datepicker-time',true).first();
23486         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23487         
23488         this.picker().on('mousedown', this.onMousedown, this);
23489         this.picker().on('click', this.onClick, this);
23490         
23491         this.picker().addClass('datepicker-dropdown');
23492     
23493         this.fillTime();
23494         this.update();
23495             
23496         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23497         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23498         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23499         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23500         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23501         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23502
23503     },
23504     
23505     fireKey: function(e){
23506         if (!this.picker().isVisible()){
23507             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23508                 this.show();
23509             }
23510             return;
23511         }
23512
23513         e.preventDefault();
23514         
23515         switch(e.keyCode){
23516             case 27: // escape
23517                 this.hide();
23518                 break;
23519             case 37: // left
23520             case 39: // right
23521                 this.onTogglePeriod();
23522                 break;
23523             case 38: // up
23524                 this.onIncrementMinutes();
23525                 break;
23526             case 40: // down
23527                 this.onDecrementMinutes();
23528                 break;
23529             case 13: // enter
23530             case 9: // tab
23531                 this.setTime();
23532                 break;
23533         }
23534     },
23535     
23536     onClick: function(e) {
23537         e.stopPropagation();
23538         e.preventDefault();
23539     },
23540     
23541     picker : function()
23542     {
23543         return this.pickerEl;
23544     },
23545     
23546     fillTime: function()
23547     {    
23548         var time = this.pop.select('tbody', true).first();
23549         
23550         time.dom.innerHTML = '';
23551         
23552         time.createChild({
23553             tag: 'tr',
23554             cn: [
23555                 {
23556                     tag: 'td',
23557                     cn: [
23558                         {
23559                             tag: 'a',
23560                             href: '#',
23561                             cls: 'btn',
23562                             cn: [
23563                                 {
23564                                     tag: 'i',
23565                                     cls: 'hours-up fa fas fa-chevron-up'
23566                                 }
23567                             ]
23568                         } 
23569                     ]
23570                 },
23571                 {
23572                     tag: 'td',
23573                     cls: 'separator'
23574                 },
23575                 {
23576                     tag: 'td',
23577                     cn: [
23578                         {
23579                             tag: 'a',
23580                             href: '#',
23581                             cls: 'btn',
23582                             cn: [
23583                                 {
23584                                     tag: 'i',
23585                                     cls: 'minutes-up fa fas fa-chevron-up'
23586                                 }
23587                             ]
23588                         }
23589                     ]
23590                 },
23591                 {
23592                     tag: 'td',
23593                     cls: 'separator'
23594                 }
23595             ]
23596         });
23597         
23598         time.createChild({
23599             tag: 'tr',
23600             cn: [
23601                 {
23602                     tag: 'td',
23603                     cn: [
23604                         {
23605                             tag: 'span',
23606                             cls: 'timepicker-hour',
23607                             html: '00'
23608                         }  
23609                     ]
23610                 },
23611                 {
23612                     tag: 'td',
23613                     cls: 'separator',
23614                     html: ':'
23615                 },
23616                 {
23617                     tag: 'td',
23618                     cn: [
23619                         {
23620                             tag: 'span',
23621                             cls: 'timepicker-minute',
23622                             html: '00'
23623                         }  
23624                     ]
23625                 },
23626                 {
23627                     tag: 'td',
23628                     cls: 'separator'
23629                 },
23630                 {
23631                     tag: 'td',
23632                     cn: [
23633                         {
23634                             tag: 'button',
23635                             type: 'button',
23636                             cls: 'btn btn-primary period',
23637                             html: 'AM'
23638                             
23639                         }
23640                     ]
23641                 }
23642             ]
23643         });
23644         
23645         time.createChild({
23646             tag: 'tr',
23647             cn: [
23648                 {
23649                     tag: 'td',
23650                     cn: [
23651                         {
23652                             tag: 'a',
23653                             href: '#',
23654                             cls: 'btn',
23655                             cn: [
23656                                 {
23657                                     tag: 'span',
23658                                     cls: 'hours-down fa fas fa-chevron-down'
23659                                 }
23660                             ]
23661                         }
23662                     ]
23663                 },
23664                 {
23665                     tag: 'td',
23666                     cls: 'separator'
23667                 },
23668                 {
23669                     tag: 'td',
23670                     cn: [
23671                         {
23672                             tag: 'a',
23673                             href: '#',
23674                             cls: 'btn',
23675                             cn: [
23676                                 {
23677                                     tag: 'span',
23678                                     cls: 'minutes-down fa fas fa-chevron-down'
23679                                 }
23680                             ]
23681                         }
23682                     ]
23683                 },
23684                 {
23685                     tag: 'td',
23686                     cls: 'separator'
23687                 }
23688             ]
23689         });
23690         
23691     },
23692     
23693     update: function()
23694     {
23695         
23696         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23697         
23698         this.fill();
23699     },
23700     
23701     fill: function() 
23702     {
23703         var hours = this.time.getHours();
23704         var minutes = this.time.getMinutes();
23705         var period = 'AM';
23706         
23707         if(hours > 11){
23708             period = 'PM';
23709         }
23710         
23711         if(hours == 0){
23712             hours = 12;
23713         }
23714         
23715         
23716         if(hours > 12){
23717             hours = hours - 12;
23718         }
23719         
23720         if(hours < 10){
23721             hours = '0' + hours;
23722         }
23723         
23724         if(minutes < 10){
23725             minutes = '0' + minutes;
23726         }
23727         
23728         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23729         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23730         this.pop.select('button', true).first().dom.innerHTML = period;
23731         
23732     },
23733     
23734     place: function()
23735     {   
23736         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23737         
23738         var cls = ['bottom'];
23739         
23740         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23741             cls.pop();
23742             cls.push('top');
23743         }
23744         
23745         cls.push('right');
23746         
23747         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23748             cls.pop();
23749             cls.push('left');
23750         }
23751         //this.picker().setXY(20000,20000);
23752         this.picker().addClass(cls.join('-'));
23753         
23754         var _this = this;
23755         
23756         Roo.each(cls, function(c){
23757             if(c == 'bottom'){
23758                 (function() {
23759                  //  
23760                 }).defer(200);
23761                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23762                 //_this.picker().setTop(_this.inputEl().getHeight());
23763                 return;
23764             }
23765             if(c == 'top'){
23766                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23767                 
23768                 //_this.picker().setTop(0 - _this.picker().getHeight());
23769                 return;
23770             }
23771             /*
23772             if(c == 'left'){
23773                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23774                 return;
23775             }
23776             if(c == 'right'){
23777                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23778                 return;
23779             }
23780             */
23781         });
23782         
23783     },
23784   
23785     onFocus : function()
23786     {
23787         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23788         this.show();
23789     },
23790     
23791     onBlur : function()
23792     {
23793         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23794         this.hide();
23795     },
23796     
23797     show : function()
23798     {
23799         this.picker().show();
23800         this.pop.show();
23801         this.update();
23802         this.place();
23803         
23804         this.fireEvent('show', this, this.date);
23805     },
23806     
23807     hide : function()
23808     {
23809         this.picker().hide();
23810         this.pop.hide();
23811         
23812         this.fireEvent('hide', this, this.date);
23813     },
23814     
23815     setTime : function()
23816     {
23817         this.hide();
23818         this.setValue(this.time.format(this.format));
23819         
23820         this.fireEvent('select', this, this.date);
23821         
23822         
23823     },
23824     
23825     onMousedown: function(e){
23826         e.stopPropagation();
23827         e.preventDefault();
23828     },
23829     
23830     onIncrementHours: function()
23831     {
23832         Roo.log('onIncrementHours');
23833         this.time = this.time.add(Date.HOUR, 1);
23834         this.update();
23835         
23836     },
23837     
23838     onDecrementHours: function()
23839     {
23840         Roo.log('onDecrementHours');
23841         this.time = this.time.add(Date.HOUR, -1);
23842         this.update();
23843     },
23844     
23845     onIncrementMinutes: function()
23846     {
23847         Roo.log('onIncrementMinutes');
23848         this.time = this.time.add(Date.MINUTE, 1);
23849         this.update();
23850     },
23851     
23852     onDecrementMinutes: function()
23853     {
23854         Roo.log('onDecrementMinutes');
23855         this.time = this.time.add(Date.MINUTE, -1);
23856         this.update();
23857     },
23858     
23859     onTogglePeriod: function()
23860     {
23861         Roo.log('onTogglePeriod');
23862         this.time = this.time.add(Date.HOUR, 12);
23863         this.update();
23864     }
23865     
23866    
23867 });
23868  
23869
23870 Roo.apply(Roo.bootstrap.TimeField,  {
23871   
23872     template : {
23873         tag: 'div',
23874         cls: 'datepicker dropdown-menu',
23875         cn: [
23876             {
23877                 tag: 'div',
23878                 cls: 'datepicker-time',
23879                 cn: [
23880                 {
23881                     tag: 'table',
23882                     cls: 'table-condensed',
23883                     cn:[
23884                         {
23885                             tag: 'tbody',
23886                             cn: [
23887                                 {
23888                                     tag: 'tr',
23889                                     cn: [
23890                                     {
23891                                         tag: 'td',
23892                                         colspan: '7'
23893                                     }
23894                                     ]
23895                                 }
23896                             ]
23897                         },
23898                         {
23899                             tag: 'tfoot',
23900                             cn: [
23901                                 {
23902                                     tag: 'tr',
23903                                     cn: [
23904                                     {
23905                                         tag: 'th',
23906                                         colspan: '7',
23907                                         cls: '',
23908                                         cn: [
23909                                             {
23910                                                 tag: 'button',
23911                                                 cls: 'btn btn-info ok',
23912                                                 html: 'OK'
23913                                             }
23914                                         ]
23915                                     }
23916                     
23917                                     ]
23918                                 }
23919                             ]
23920                         }
23921                     ]
23922                 }
23923                 ]
23924             }
23925         ]
23926     }
23927 });
23928
23929  
23930
23931  /*
23932  * - LGPL
23933  *
23934  * MonthField
23935  * 
23936  */
23937
23938 /**
23939  * @class Roo.bootstrap.MonthField
23940  * @extends Roo.bootstrap.Input
23941  * Bootstrap MonthField class
23942  * 
23943  * @cfg {String} language default en
23944  * 
23945  * @constructor
23946  * Create a new MonthField
23947  * @param {Object} config The config object
23948  */
23949
23950 Roo.bootstrap.MonthField = function(config){
23951     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23952     
23953     this.addEvents({
23954         /**
23955          * @event show
23956          * Fires when this field show.
23957          * @param {Roo.bootstrap.MonthField} this
23958          * @param {Mixed} date The date value
23959          */
23960         show : true,
23961         /**
23962          * @event show
23963          * Fires when this field hide.
23964          * @param {Roo.bootstrap.MonthField} this
23965          * @param {Mixed} date The date value
23966          */
23967         hide : true,
23968         /**
23969          * @event select
23970          * Fires when select a date.
23971          * @param {Roo.bootstrap.MonthField} this
23972          * @param {String} oldvalue The old value
23973          * @param {String} newvalue The new value
23974          */
23975         select : true
23976     });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23980     
23981     onRender: function(ct, position)
23982     {
23983         
23984         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23985         
23986         this.language = this.language || 'en';
23987         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23988         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23989         
23990         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23991         this.isInline = false;
23992         this.isInput = true;
23993         this.component = this.el.select('.add-on', true).first() || false;
23994         this.component = (this.component && this.component.length === 0) ? false : this.component;
23995         this.hasInput = this.component && this.inputEL().length;
23996         
23997         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23998         
23999         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24000         
24001         this.picker().on('mousedown', this.onMousedown, this);
24002         this.picker().on('click', this.onClick, this);
24003         
24004         this.picker().addClass('datepicker-dropdown');
24005         
24006         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24007             v.setStyle('width', '189px');
24008         });
24009         
24010         this.fillMonths();
24011         
24012         this.update();
24013         
24014         if(this.isInline) {
24015             this.show();
24016         }
24017         
24018     },
24019     
24020     setValue: function(v, suppressEvent)
24021     {   
24022         var o = this.getValue();
24023         
24024         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24025         
24026         this.update();
24027
24028         if(suppressEvent !== true){
24029             this.fireEvent('select', this, o, v);
24030         }
24031         
24032     },
24033     
24034     getValue: function()
24035     {
24036         return this.value;
24037     },
24038     
24039     onClick: function(e) 
24040     {
24041         e.stopPropagation();
24042         e.preventDefault();
24043         
24044         var target = e.getTarget();
24045         
24046         if(target.nodeName.toLowerCase() === 'i'){
24047             target = Roo.get(target).dom.parentNode;
24048         }
24049         
24050         var nodeName = target.nodeName;
24051         var className = target.className;
24052         var html = target.innerHTML;
24053         
24054         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24055             return;
24056         }
24057         
24058         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24059         
24060         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24061         
24062         this.hide();
24063                         
24064     },
24065     
24066     picker : function()
24067     {
24068         return this.pickerEl;
24069     },
24070     
24071     fillMonths: function()
24072     {    
24073         var i = 0;
24074         var months = this.picker().select('>.datepicker-months td', true).first();
24075         
24076         months.dom.innerHTML = '';
24077         
24078         while (i < 12) {
24079             var month = {
24080                 tag: 'span',
24081                 cls: 'month',
24082                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24083             };
24084             
24085             months.createChild(month);
24086         }
24087         
24088     },
24089     
24090     update: function()
24091     {
24092         var _this = this;
24093         
24094         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24095             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24096         }
24097         
24098         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24099             e.removeClass('active');
24100             
24101             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24102                 e.addClass('active');
24103             }
24104         })
24105     },
24106     
24107     place: function()
24108     {
24109         if(this.isInline) {
24110             return;
24111         }
24112         
24113         this.picker().removeClass(['bottom', 'top']);
24114         
24115         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24116             /*
24117              * place to the top of element!
24118              *
24119              */
24120             
24121             this.picker().addClass('top');
24122             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24123             
24124             return;
24125         }
24126         
24127         this.picker().addClass('bottom');
24128         
24129         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24130     },
24131     
24132     onFocus : function()
24133     {
24134         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24135         this.show();
24136     },
24137     
24138     onBlur : function()
24139     {
24140         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24141         
24142         var d = this.inputEl().getValue();
24143         
24144         this.setValue(d);
24145                 
24146         this.hide();
24147     },
24148     
24149     show : function()
24150     {
24151         this.picker().show();
24152         this.picker().select('>.datepicker-months', true).first().show();
24153         this.update();
24154         this.place();
24155         
24156         this.fireEvent('show', this, this.date);
24157     },
24158     
24159     hide : function()
24160     {
24161         if(this.isInline) {
24162             return;
24163         }
24164         this.picker().hide();
24165         this.fireEvent('hide', this, this.date);
24166         
24167     },
24168     
24169     onMousedown: function(e)
24170     {
24171         e.stopPropagation();
24172         e.preventDefault();
24173     },
24174     
24175     keyup: function(e)
24176     {
24177         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24178         this.update();
24179     },
24180
24181     fireKey: function(e)
24182     {
24183         if (!this.picker().isVisible()){
24184             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24185                 this.show();
24186             }
24187             return;
24188         }
24189         
24190         var dir;
24191         
24192         switch(e.keyCode){
24193             case 27: // escape
24194                 this.hide();
24195                 e.preventDefault();
24196                 break;
24197             case 37: // left
24198             case 39: // right
24199                 dir = e.keyCode == 37 ? -1 : 1;
24200                 
24201                 this.vIndex = this.vIndex + dir;
24202                 
24203                 if(this.vIndex < 0){
24204                     this.vIndex = 0;
24205                 }
24206                 
24207                 if(this.vIndex > 11){
24208                     this.vIndex = 11;
24209                 }
24210                 
24211                 if(isNaN(this.vIndex)){
24212                     this.vIndex = 0;
24213                 }
24214                 
24215                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24216                 
24217                 break;
24218             case 38: // up
24219             case 40: // down
24220                 
24221                 dir = e.keyCode == 38 ? -1 : 1;
24222                 
24223                 this.vIndex = this.vIndex + dir * 4;
24224                 
24225                 if(this.vIndex < 0){
24226                     this.vIndex = 0;
24227                 }
24228                 
24229                 if(this.vIndex > 11){
24230                     this.vIndex = 11;
24231                 }
24232                 
24233                 if(isNaN(this.vIndex)){
24234                     this.vIndex = 0;
24235                 }
24236                 
24237                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24238                 break;
24239                 
24240             case 13: // enter
24241                 
24242                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24243                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24244                 }
24245                 
24246                 this.hide();
24247                 e.preventDefault();
24248                 break;
24249             case 9: // tab
24250                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24251                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24252                 }
24253                 this.hide();
24254                 break;
24255             case 16: // shift
24256             case 17: // ctrl
24257             case 18: // alt
24258                 break;
24259             default :
24260                 this.hide();
24261                 
24262         }
24263     },
24264     
24265     remove: function() 
24266     {
24267         this.picker().remove();
24268     }
24269    
24270 });
24271
24272 Roo.apply(Roo.bootstrap.MonthField,  {
24273     
24274     content : {
24275         tag: 'tbody',
24276         cn: [
24277         {
24278             tag: 'tr',
24279             cn: [
24280             {
24281                 tag: 'td',
24282                 colspan: '7'
24283             }
24284             ]
24285         }
24286         ]
24287     },
24288     
24289     dates:{
24290         en: {
24291             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24292             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24293         }
24294     }
24295 });
24296
24297 Roo.apply(Roo.bootstrap.MonthField,  {
24298   
24299     template : {
24300         tag: 'div',
24301         cls: 'datepicker dropdown-menu roo-dynamic',
24302         cn: [
24303             {
24304                 tag: 'div',
24305                 cls: 'datepicker-months',
24306                 cn: [
24307                 {
24308                     tag: 'table',
24309                     cls: 'table-condensed',
24310                     cn:[
24311                         Roo.bootstrap.DateField.content
24312                     ]
24313                 }
24314                 ]
24315             }
24316         ]
24317     }
24318 });
24319
24320  
24321
24322  
24323  /*
24324  * - LGPL
24325  *
24326  * CheckBox
24327  * 
24328  */
24329
24330 /**
24331  * @class Roo.bootstrap.CheckBox
24332  * @extends Roo.bootstrap.Input
24333  * Bootstrap CheckBox class
24334  * 
24335  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24336  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24337  * @cfg {String} boxLabel The text that appears beside the checkbox
24338  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24339  * @cfg {Boolean} checked initnal the element
24340  * @cfg {Boolean} inline inline the element (default false)
24341  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24342  * @cfg {String} tooltip label tooltip
24343  * 
24344  * @constructor
24345  * Create a new CheckBox
24346  * @param {Object} config The config object
24347  */
24348
24349 Roo.bootstrap.CheckBox = function(config){
24350     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24351    
24352     this.addEvents({
24353         /**
24354         * @event check
24355         * Fires when the element is checked or unchecked.
24356         * @param {Roo.bootstrap.CheckBox} this This input
24357         * @param {Boolean} checked The new checked value
24358         */
24359        check : true,
24360        /**
24361         * @event click
24362         * Fires when the element is click.
24363         * @param {Roo.bootstrap.CheckBox} this This input
24364         */
24365        click : true
24366     });
24367     
24368 };
24369
24370 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24371   
24372     inputType: 'checkbox',
24373     inputValue: 1,
24374     valueOff: 0,
24375     boxLabel: false,
24376     checked: false,
24377     weight : false,
24378     inline: false,
24379     tooltip : '',
24380     
24381     // checkbox success does not make any sense really.. 
24382     invalidClass : "",
24383     validClass : "",
24384     
24385     
24386     getAutoCreate : function()
24387     {
24388         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24389         
24390         var id = Roo.id();
24391         
24392         var cfg = {};
24393         
24394         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24395         
24396         if(this.inline){
24397             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24398         }
24399         
24400         var input =  {
24401             tag: 'input',
24402             id : id,
24403             type : this.inputType,
24404             value : this.inputValue,
24405             cls : 'roo-' + this.inputType, //'form-box',
24406             placeholder : this.placeholder || ''
24407             
24408         };
24409         
24410         if(this.inputType != 'radio'){
24411             var hidden =  {
24412                 tag: 'input',
24413                 type : 'hidden',
24414                 cls : 'roo-hidden-value',
24415                 value : this.checked ? this.inputValue : this.valueOff
24416             };
24417         }
24418         
24419             
24420         if (this.weight) { // Validity check?
24421             cfg.cls += " " + this.inputType + "-" + this.weight;
24422         }
24423         
24424         if (this.disabled) {
24425             input.disabled=true;
24426         }
24427         
24428         if(this.checked){
24429             input.checked = this.checked;
24430         }
24431         
24432         if (this.name) {
24433             
24434             input.name = this.name;
24435             
24436             if(this.inputType != 'radio'){
24437                 hidden.name = this.name;
24438                 input.name = '_hidden_' + this.name;
24439             }
24440         }
24441         
24442         if (this.size) {
24443             input.cls += ' input-' + this.size;
24444         }
24445         
24446         var settings=this;
24447         
24448         ['xs','sm','md','lg'].map(function(size){
24449             if (settings[size]) {
24450                 cfg.cls += ' col-' + size + '-' + settings[size];
24451             }
24452         });
24453         
24454         var inputblock = input;
24455          
24456         if (this.before || this.after) {
24457             
24458             inputblock = {
24459                 cls : 'input-group',
24460                 cn :  [] 
24461             };
24462             
24463             if (this.before) {
24464                 inputblock.cn.push({
24465                     tag :'span',
24466                     cls : 'input-group-addon',
24467                     html : this.before
24468                 });
24469             }
24470             
24471             inputblock.cn.push(input);
24472             
24473             if(this.inputType != 'radio'){
24474                 inputblock.cn.push(hidden);
24475             }
24476             
24477             if (this.after) {
24478                 inputblock.cn.push({
24479                     tag :'span',
24480                     cls : 'input-group-addon',
24481                     html : this.after
24482                 });
24483             }
24484             
24485         }
24486         var boxLabelCfg = false;
24487         
24488         if(this.boxLabel){
24489            
24490             boxLabelCfg = {
24491                 tag: 'label',
24492                 //'for': id, // box label is handled by onclick - so no for...
24493                 cls: 'box-label',
24494                 html: this.boxLabel
24495             };
24496             if(this.tooltip){
24497                 boxLabelCfg.tooltip = this.tooltip;
24498             }
24499              
24500         }
24501         
24502         
24503         if (align ==='left' && this.fieldLabel.length) {
24504 //                Roo.log("left and has label");
24505             cfg.cn = [
24506                 {
24507                     tag: 'label',
24508                     'for' :  id,
24509                     cls : 'control-label',
24510                     html : this.fieldLabel
24511                 },
24512                 {
24513                     cls : "", 
24514                     cn: [
24515                         inputblock
24516                     ]
24517                 }
24518             ];
24519             
24520             if (boxLabelCfg) {
24521                 cfg.cn[1].cn.push(boxLabelCfg);
24522             }
24523             
24524             if(this.labelWidth > 12){
24525                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24526             }
24527             
24528             if(this.labelWidth < 13 && this.labelmd == 0){
24529                 this.labelmd = this.labelWidth;
24530             }
24531             
24532             if(this.labellg > 0){
24533                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24534                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24535             }
24536             
24537             if(this.labelmd > 0){
24538                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24539                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24540             }
24541             
24542             if(this.labelsm > 0){
24543                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24544                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24545             }
24546             
24547             if(this.labelxs > 0){
24548                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24549                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24550             }
24551             
24552         } else if ( this.fieldLabel.length) {
24553 //                Roo.log(" label");
24554                 cfg.cn = [
24555                    
24556                     {
24557                         tag: this.boxLabel ? 'span' : 'label',
24558                         'for': id,
24559                         cls: 'control-label box-input-label',
24560                         //cls : 'input-group-addon',
24561                         html : this.fieldLabel
24562                     },
24563                     
24564                     inputblock
24565                     
24566                 ];
24567                 if (boxLabelCfg) {
24568                     cfg.cn.push(boxLabelCfg);
24569                 }
24570
24571         } else {
24572             
24573 //                Roo.log(" no label && no align");
24574                 cfg.cn = [  inputblock ] ;
24575                 if (boxLabelCfg) {
24576                     cfg.cn.push(boxLabelCfg);
24577                 }
24578
24579                 
24580         }
24581         
24582        
24583         
24584         if(this.inputType != 'radio'){
24585             cfg.cn.push(hidden);
24586         }
24587         
24588         return cfg;
24589         
24590     },
24591     
24592     /**
24593      * return the real input element.
24594      */
24595     inputEl: function ()
24596     {
24597         return this.el.select('input.roo-' + this.inputType,true).first();
24598     },
24599     hiddenEl: function ()
24600     {
24601         return this.el.select('input.roo-hidden-value',true).first();
24602     },
24603     
24604     labelEl: function()
24605     {
24606         return this.el.select('label.control-label',true).first();
24607     },
24608     /* depricated... */
24609     
24610     label: function()
24611     {
24612         return this.labelEl();
24613     },
24614     
24615     boxLabelEl: function()
24616     {
24617         return this.el.select('label.box-label',true).first();
24618     },
24619     
24620     initEvents : function()
24621     {
24622 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24623         
24624         this.inputEl().on('click', this.onClick,  this);
24625         
24626         if (this.boxLabel) { 
24627             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24628         }
24629         
24630         this.startValue = this.getValue();
24631         
24632         if(this.groupId){
24633             Roo.bootstrap.CheckBox.register(this);
24634         }
24635     },
24636     
24637     onClick : function(e)
24638     {   
24639         if(this.fireEvent('click', this, e) !== false){
24640             this.setChecked(!this.checked);
24641         }
24642         
24643     },
24644     
24645     setChecked : function(state,suppressEvent)
24646     {
24647         this.startValue = this.getValue();
24648
24649         if(this.inputType == 'radio'){
24650             
24651             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24652                 e.dom.checked = false;
24653             });
24654             
24655             this.inputEl().dom.checked = true;
24656             
24657             this.inputEl().dom.value = this.inputValue;
24658             
24659             if(suppressEvent !== true){
24660                 this.fireEvent('check', this, true);
24661             }
24662             
24663             this.validate();
24664             
24665             return;
24666         }
24667         
24668         this.checked = state;
24669         
24670         this.inputEl().dom.checked = state;
24671         
24672         
24673         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24674         
24675         if(suppressEvent !== true){
24676             this.fireEvent('check', this, state);
24677         }
24678         
24679         this.validate();
24680     },
24681     
24682     getValue : function()
24683     {
24684         if(this.inputType == 'radio'){
24685             return this.getGroupValue();
24686         }
24687         
24688         return this.hiddenEl().dom.value;
24689         
24690     },
24691     
24692     getGroupValue : function()
24693     {
24694         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24695             return '';
24696         }
24697         
24698         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24699     },
24700     
24701     setValue : function(v,suppressEvent)
24702     {
24703         if(this.inputType == 'radio'){
24704             this.setGroupValue(v, suppressEvent);
24705             return;
24706         }
24707         
24708         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24709         
24710         this.validate();
24711     },
24712     
24713     setGroupValue : function(v, suppressEvent)
24714     {
24715         this.startValue = this.getValue();
24716         
24717         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24718             e.dom.checked = false;
24719             
24720             if(e.dom.value == v){
24721                 e.dom.checked = true;
24722             }
24723         });
24724         
24725         if(suppressEvent !== true){
24726             this.fireEvent('check', this, true);
24727         }
24728
24729         this.validate();
24730         
24731         return;
24732     },
24733     
24734     validate : function()
24735     {
24736         if(this.getVisibilityEl().hasClass('hidden')){
24737             return true;
24738         }
24739         
24740         if(
24741                 this.disabled || 
24742                 (this.inputType == 'radio' && this.validateRadio()) ||
24743                 (this.inputType == 'checkbox' && this.validateCheckbox())
24744         ){
24745             this.markValid();
24746             return true;
24747         }
24748         
24749         this.markInvalid();
24750         return false;
24751     },
24752     
24753     validateRadio : function()
24754     {
24755         if(this.getVisibilityEl().hasClass('hidden')){
24756             return true;
24757         }
24758         
24759         if(this.allowBlank){
24760             return true;
24761         }
24762         
24763         var valid = false;
24764         
24765         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24766             if(!e.dom.checked){
24767                 return;
24768             }
24769             
24770             valid = true;
24771             
24772             return false;
24773         });
24774         
24775         return valid;
24776     },
24777     
24778     validateCheckbox : function()
24779     {
24780         if(!this.groupId){
24781             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24782             //return (this.getValue() == this.inputValue) ? true : false;
24783         }
24784         
24785         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24786         
24787         if(!group){
24788             return false;
24789         }
24790         
24791         var r = false;
24792         
24793         for(var i in group){
24794             if(group[i].el.isVisible(true)){
24795                 r = false;
24796                 break;
24797             }
24798             
24799             r = true;
24800         }
24801         
24802         for(var i in group){
24803             if(r){
24804                 break;
24805             }
24806             
24807             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24808         }
24809         
24810         return r;
24811     },
24812     
24813     /**
24814      * Mark this field as valid
24815      */
24816     markValid : function()
24817     {
24818         var _this = this;
24819         
24820         this.fireEvent('valid', this);
24821         
24822         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24823         
24824         if(this.groupId){
24825             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24826         }
24827         
24828         if(label){
24829             label.markValid();
24830         }
24831
24832         if(this.inputType == 'radio'){
24833             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24834                 var fg = e.findParent('.form-group', false, true);
24835                 if (Roo.bootstrap.version == 3) {
24836                     fg.removeClass([_this.invalidClass, _this.validClass]);
24837                     fg.addClass(_this.validClass);
24838                 } else {
24839                     fg.removeClass(['is-valid', 'is-invalid']);
24840                     fg.addClass('is-valid');
24841                 }
24842             });
24843             
24844             return;
24845         }
24846
24847         if(!this.groupId){
24848             var fg = this.el.findParent('.form-group', false, true);
24849             if (Roo.bootstrap.version == 3) {
24850                 fg.removeClass([this.invalidClass, this.validClass]);
24851                 fg.addClass(this.validClass);
24852             } else {
24853                 fg.removeClass(['is-valid', 'is-invalid']);
24854                 fg.addClass('is-valid');
24855             }
24856             return;
24857         }
24858         
24859         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24860         
24861         if(!group){
24862             return;
24863         }
24864         
24865         for(var i in group){
24866             var fg = group[i].el.findParent('.form-group', false, true);
24867             if (Roo.bootstrap.version == 3) {
24868                 fg.removeClass([this.invalidClass, this.validClass]);
24869                 fg.addClass(this.validClass);
24870             } else {
24871                 fg.removeClass(['is-valid', 'is-invalid']);
24872                 fg.addClass('is-valid');
24873             }
24874         }
24875     },
24876     
24877      /**
24878      * Mark this field as invalid
24879      * @param {String} msg The validation message
24880      */
24881     markInvalid : function(msg)
24882     {
24883         if(this.allowBlank){
24884             return;
24885         }
24886         
24887         var _this = this;
24888         
24889         this.fireEvent('invalid', this, msg);
24890         
24891         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24892         
24893         if(this.groupId){
24894             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24895         }
24896         
24897         if(label){
24898             label.markInvalid();
24899         }
24900             
24901         if(this.inputType == 'radio'){
24902             
24903             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24904                 var fg = e.findParent('.form-group', false, true);
24905                 if (Roo.bootstrap.version == 3) {
24906                     fg.removeClass([_this.invalidClass, _this.validClass]);
24907                     fg.addClass(_this.invalidClass);
24908                 } else {
24909                     fg.removeClass(['is-invalid', 'is-valid']);
24910                     fg.addClass('is-invalid');
24911                 }
24912             });
24913             
24914             return;
24915         }
24916         
24917         if(!this.groupId){
24918             var fg = this.el.findParent('.form-group', false, true);
24919             if (Roo.bootstrap.version == 3) {
24920                 fg.removeClass([_this.invalidClass, _this.validClass]);
24921                 fg.addClass(_this.invalidClass);
24922             } else {
24923                 fg.removeClass(['is-invalid', 'is-valid']);
24924                 fg.addClass('is-invalid');
24925             }
24926             return;
24927         }
24928         
24929         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24930         
24931         if(!group){
24932             return;
24933         }
24934         
24935         for(var i in group){
24936             var fg = group[i].el.findParent('.form-group', false, true);
24937             if (Roo.bootstrap.version == 3) {
24938                 fg.removeClass([_this.invalidClass, _this.validClass]);
24939                 fg.addClass(_this.invalidClass);
24940             } else {
24941                 fg.removeClass(['is-invalid', 'is-valid']);
24942                 fg.addClass('is-invalid');
24943             }
24944         }
24945         
24946     },
24947     
24948     clearInvalid : function()
24949     {
24950         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24951         
24952         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24953         
24954         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24955         
24956         if (label && label.iconEl) {
24957             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24958             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24959         }
24960     },
24961     
24962     disable : function()
24963     {
24964         if(this.inputType != 'radio'){
24965             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24966             return;
24967         }
24968         
24969         var _this = this;
24970         
24971         if(this.rendered){
24972             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24973                 _this.getActionEl().addClass(this.disabledClass);
24974                 e.dom.disabled = true;
24975             });
24976         }
24977         
24978         this.disabled = true;
24979         this.fireEvent("disable", this);
24980         return this;
24981     },
24982
24983     enable : function()
24984     {
24985         if(this.inputType != 'radio'){
24986             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24987             return;
24988         }
24989         
24990         var _this = this;
24991         
24992         if(this.rendered){
24993             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24994                 _this.getActionEl().removeClass(this.disabledClass);
24995                 e.dom.disabled = false;
24996             });
24997         }
24998         
24999         this.disabled = false;
25000         this.fireEvent("enable", this);
25001         return this;
25002     },
25003     
25004     setBoxLabel : function(v)
25005     {
25006         this.boxLabel = v;
25007         
25008         if(this.rendered){
25009             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25010         }
25011     }
25012
25013 });
25014
25015 Roo.apply(Roo.bootstrap.CheckBox, {
25016     
25017     groups: {},
25018     
25019      /**
25020     * register a CheckBox Group
25021     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25022     */
25023     register : function(checkbox)
25024     {
25025         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25026             this.groups[checkbox.groupId] = {};
25027         }
25028         
25029         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25030             return;
25031         }
25032         
25033         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25034         
25035     },
25036     /**
25037     * fetch a CheckBox Group based on the group ID
25038     * @param {string} the group ID
25039     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25040     */
25041     get: function(groupId) {
25042         if (typeof(this.groups[groupId]) == 'undefined') {
25043             return false;
25044         }
25045         
25046         return this.groups[groupId] ;
25047     }
25048     
25049     
25050 });
25051 /*
25052  * - LGPL
25053  *
25054  * RadioItem
25055  * 
25056  */
25057
25058 /**
25059  * @class Roo.bootstrap.Radio
25060  * @extends Roo.bootstrap.Component
25061  * Bootstrap Radio class
25062  * @cfg {String} boxLabel - the label associated
25063  * @cfg {String} value - the value of radio
25064  * 
25065  * @constructor
25066  * Create a new Radio
25067  * @param {Object} config The config object
25068  */
25069 Roo.bootstrap.Radio = function(config){
25070     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25071     
25072 };
25073
25074 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25075     
25076     boxLabel : '',
25077     
25078     value : '',
25079     
25080     getAutoCreate : function()
25081     {
25082         var cfg = {
25083             tag : 'div',
25084             cls : 'form-group radio',
25085             cn : [
25086                 {
25087                     tag : 'label',
25088                     cls : 'box-label',
25089                     html : this.boxLabel
25090                 }
25091             ]
25092         };
25093         
25094         return cfg;
25095     },
25096     
25097     initEvents : function() 
25098     {
25099         this.parent().register(this);
25100         
25101         this.el.on('click', this.onClick, this);
25102         
25103     },
25104     
25105     onClick : function(e)
25106     {
25107         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25108             this.setChecked(true);
25109         }
25110     },
25111     
25112     setChecked : function(state, suppressEvent)
25113     {
25114         this.parent().setValue(this.value, suppressEvent);
25115         
25116     },
25117     
25118     setBoxLabel : function(v)
25119     {
25120         this.boxLabel = v;
25121         
25122         if(this.rendered){
25123             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25124         }
25125     }
25126     
25127 });
25128  
25129
25130  /*
25131  * - LGPL
25132  *
25133  * Input
25134  * 
25135  */
25136
25137 /**
25138  * @class Roo.bootstrap.SecurePass
25139  * @extends Roo.bootstrap.Input
25140  * Bootstrap SecurePass class
25141  *
25142  * 
25143  * @constructor
25144  * Create a new SecurePass
25145  * @param {Object} config The config object
25146  */
25147  
25148 Roo.bootstrap.SecurePass = function (config) {
25149     // these go here, so the translation tool can replace them..
25150     this.errors = {
25151         PwdEmpty: "Please type a password, and then retype it to confirm.",
25152         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25153         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25154         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25155         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25156         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25157         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25158         TooWeak: "Your password is Too Weak."
25159     },
25160     this.meterLabel = "Password strength:";
25161     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25162     this.meterClass = [
25163         "roo-password-meter-tooweak", 
25164         "roo-password-meter-weak", 
25165         "roo-password-meter-medium", 
25166         "roo-password-meter-strong", 
25167         "roo-password-meter-grey"
25168     ];
25169     
25170     this.errors = {};
25171     
25172     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25173 }
25174
25175 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25176     /**
25177      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25178      * {
25179      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25180      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25181      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25182      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25183      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25184      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25185      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25186      * })
25187      */
25188     // private
25189     
25190     meterWidth: 300,
25191     errorMsg :'',    
25192     errors: false,
25193     imageRoot: '/',
25194     /**
25195      * @cfg {String/Object} Label for the strength meter (defaults to
25196      * 'Password strength:')
25197      */
25198     // private
25199     meterLabel: '',
25200     /**
25201      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25202      * ['Weak', 'Medium', 'Strong'])
25203      */
25204     // private    
25205     pwdStrengths: false,    
25206     // private
25207     strength: 0,
25208     // private
25209     _lastPwd: null,
25210     // private
25211     kCapitalLetter: 0,
25212     kSmallLetter: 1,
25213     kDigit: 2,
25214     kPunctuation: 3,
25215     
25216     insecure: false,
25217     // private
25218     initEvents: function ()
25219     {
25220         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25221
25222         if (this.el.is('input[type=password]') && Roo.isSafari) {
25223             this.el.on('keydown', this.SafariOnKeyDown, this);
25224         }
25225
25226         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25227     },
25228     // private
25229     onRender: function (ct, position)
25230     {
25231         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25232         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25233         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25234
25235         this.trigger.createChild({
25236                    cn: [
25237                     {
25238                     //id: 'PwdMeter',
25239                     tag: 'div',
25240                     cls: 'roo-password-meter-grey col-xs-12',
25241                     style: {
25242                         //width: 0,
25243                         //width: this.meterWidth + 'px'                                                
25244                         }
25245                     },
25246                     {                            
25247                          cls: 'roo-password-meter-text'                          
25248                     }
25249                 ]            
25250         });
25251
25252          
25253         if (this.hideTrigger) {
25254             this.trigger.setDisplayed(false);
25255         }
25256         this.setSize(this.width || '', this.height || '');
25257     },
25258     // private
25259     onDestroy: function ()
25260     {
25261         if (this.trigger) {
25262             this.trigger.removeAllListeners();
25263             this.trigger.remove();
25264         }
25265         if (this.wrap) {
25266             this.wrap.remove();
25267         }
25268         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25269     },
25270     // private
25271     checkStrength: function ()
25272     {
25273         var pwd = this.inputEl().getValue();
25274         if (pwd == this._lastPwd) {
25275             return;
25276         }
25277
25278         var strength;
25279         if (this.ClientSideStrongPassword(pwd)) {
25280             strength = 3;
25281         } else if (this.ClientSideMediumPassword(pwd)) {
25282             strength = 2;
25283         } else if (this.ClientSideWeakPassword(pwd)) {
25284             strength = 1;
25285         } else {
25286             strength = 0;
25287         }
25288         
25289         Roo.log('strength1: ' + strength);
25290         
25291         //var pm = this.trigger.child('div/div/div').dom;
25292         var pm = this.trigger.child('div/div');
25293         pm.removeClass(this.meterClass);
25294         pm.addClass(this.meterClass[strength]);
25295                 
25296         
25297         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25298                 
25299         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25300         
25301         this._lastPwd = pwd;
25302     },
25303     reset: function ()
25304     {
25305         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25306         
25307         this._lastPwd = '';
25308         
25309         var pm = this.trigger.child('div/div');
25310         pm.removeClass(this.meterClass);
25311         pm.addClass('roo-password-meter-grey');        
25312         
25313         
25314         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25315         
25316         pt.innerHTML = '';
25317         this.inputEl().dom.type='password';
25318     },
25319     // private
25320     validateValue: function (value)
25321     {
25322         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25323             return false;
25324         }
25325         if (value.length == 0) {
25326             if (this.allowBlank) {
25327                 this.clearInvalid();
25328                 return true;
25329             }
25330
25331             this.markInvalid(this.errors.PwdEmpty);
25332             this.errorMsg = this.errors.PwdEmpty;
25333             return false;
25334         }
25335         
25336         if(this.insecure){
25337             return true;
25338         }
25339         
25340         if (!value.match(/[\x21-\x7e]+/)) {
25341             this.markInvalid(this.errors.PwdBadChar);
25342             this.errorMsg = this.errors.PwdBadChar;
25343             return false;
25344         }
25345         if (value.length < 6) {
25346             this.markInvalid(this.errors.PwdShort);
25347             this.errorMsg = this.errors.PwdShort;
25348             return false;
25349         }
25350         if (value.length > 16) {
25351             this.markInvalid(this.errors.PwdLong);
25352             this.errorMsg = this.errors.PwdLong;
25353             return false;
25354         }
25355         var strength;
25356         if (this.ClientSideStrongPassword(value)) {
25357             strength = 3;
25358         } else if (this.ClientSideMediumPassword(value)) {
25359             strength = 2;
25360         } else if (this.ClientSideWeakPassword(value)) {
25361             strength = 1;
25362         } else {
25363             strength = 0;
25364         }
25365
25366         
25367         if (strength < 2) {
25368             //this.markInvalid(this.errors.TooWeak);
25369             this.errorMsg = this.errors.TooWeak;
25370             //return false;
25371         }
25372         
25373         
25374         console.log('strength2: ' + strength);
25375         
25376         //var pm = this.trigger.child('div/div/div').dom;
25377         
25378         var pm = this.trigger.child('div/div');
25379         pm.removeClass(this.meterClass);
25380         pm.addClass(this.meterClass[strength]);
25381                 
25382         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25383                 
25384         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25385         
25386         this.errorMsg = ''; 
25387         return true;
25388     },
25389     // private
25390     CharacterSetChecks: function (type)
25391     {
25392         this.type = type;
25393         this.fResult = false;
25394     },
25395     // private
25396     isctype: function (character, type)
25397     {
25398         switch (type) {  
25399             case this.kCapitalLetter:
25400                 if (character >= 'A' && character <= 'Z') {
25401                     return true;
25402                 }
25403                 break;
25404             
25405             case this.kSmallLetter:
25406                 if (character >= 'a' && character <= 'z') {
25407                     return true;
25408                 }
25409                 break;
25410             
25411             case this.kDigit:
25412                 if (character >= '0' && character <= '9') {
25413                     return true;
25414                 }
25415                 break;
25416             
25417             case this.kPunctuation:
25418                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25419                     return true;
25420                 }
25421                 break;
25422             
25423             default:
25424                 return false;
25425         }
25426
25427     },
25428     // private
25429     IsLongEnough: function (pwd, size)
25430     {
25431         return !(pwd == null || isNaN(size) || pwd.length < size);
25432     },
25433     // private
25434     SpansEnoughCharacterSets: function (word, nb)
25435     {
25436         if (!this.IsLongEnough(word, nb))
25437         {
25438             return false;
25439         }
25440
25441         var characterSetChecks = new Array(
25442             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25443             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25444         );
25445         
25446         for (var index = 0; index < word.length; ++index) {
25447             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25448                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25449                     characterSetChecks[nCharSet].fResult = true;
25450                     break;
25451                 }
25452             }
25453         }
25454
25455         var nCharSets = 0;
25456         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25457             if (characterSetChecks[nCharSet].fResult) {
25458                 ++nCharSets;
25459             }
25460         }
25461
25462         if (nCharSets < nb) {
25463             return false;
25464         }
25465         return true;
25466     },
25467     // private
25468     ClientSideStrongPassword: function (pwd)
25469     {
25470         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25471     },
25472     // private
25473     ClientSideMediumPassword: function (pwd)
25474     {
25475         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25476     },
25477     // private
25478     ClientSideWeakPassword: function (pwd)
25479     {
25480         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25481     }
25482           
25483 })//<script type="text/javascript">
25484
25485 /*
25486  * Based  Ext JS Library 1.1.1
25487  * Copyright(c) 2006-2007, Ext JS, LLC.
25488  * LGPL
25489  *
25490  */
25491  
25492 /**
25493  * @class Roo.HtmlEditorCore
25494  * @extends Roo.Component
25495  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25496  *
25497  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25498  */
25499
25500 Roo.HtmlEditorCore = function(config){
25501     
25502     
25503     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25504     
25505     
25506     this.addEvents({
25507         /**
25508          * @event initialize
25509          * Fires when the editor is fully initialized (including the iframe)
25510          * @param {Roo.HtmlEditorCore} this
25511          */
25512         initialize: true,
25513         /**
25514          * @event activate
25515          * Fires when the editor is first receives the focus. Any insertion must wait
25516          * until after this event.
25517          * @param {Roo.HtmlEditorCore} this
25518          */
25519         activate: true,
25520          /**
25521          * @event beforesync
25522          * Fires before the textarea is updated with content from the editor iframe. Return false
25523          * to cancel the sync.
25524          * @param {Roo.HtmlEditorCore} this
25525          * @param {String} html
25526          */
25527         beforesync: true,
25528          /**
25529          * @event beforepush
25530          * Fires before the iframe editor is updated with content from the textarea. Return false
25531          * to cancel the push.
25532          * @param {Roo.HtmlEditorCore} this
25533          * @param {String} html
25534          */
25535         beforepush: true,
25536          /**
25537          * @event sync
25538          * Fires when the textarea is updated with content from the editor iframe.
25539          * @param {Roo.HtmlEditorCore} this
25540          * @param {String} html
25541          */
25542         sync: true,
25543          /**
25544          * @event push
25545          * Fires when the iframe editor is updated with content from the textarea.
25546          * @param {Roo.HtmlEditorCore} this
25547          * @param {String} html
25548          */
25549         push: true,
25550         
25551         /**
25552          * @event editorevent
25553          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25554          * @param {Roo.HtmlEditorCore} this
25555          */
25556         editorevent: true
25557         
25558     });
25559     
25560     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25561     
25562     // defaults : white / black...
25563     this.applyBlacklists();
25564     
25565     
25566     
25567 };
25568
25569
25570 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25571
25572
25573      /**
25574      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25575      */
25576     
25577     owner : false,
25578     
25579      /**
25580      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25581      *                        Roo.resizable.
25582      */
25583     resizable : false,
25584      /**
25585      * @cfg {Number} height (in pixels)
25586      */   
25587     height: 300,
25588    /**
25589      * @cfg {Number} width (in pixels)
25590      */   
25591     width: 500,
25592     
25593     /**
25594      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25595      * 
25596      */
25597     stylesheets: false,
25598     
25599     // id of frame..
25600     frameId: false,
25601     
25602     // private properties
25603     validationEvent : false,
25604     deferHeight: true,
25605     initialized : false,
25606     activated : false,
25607     sourceEditMode : false,
25608     onFocus : Roo.emptyFn,
25609     iframePad:3,
25610     hideMode:'offsets',
25611     
25612     clearUp: true,
25613     
25614     // blacklist + whitelisted elements..
25615     black: false,
25616     white: false,
25617      
25618     bodyCls : '',
25619
25620     /**
25621      * Protected method that will not generally be called directly. It
25622      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25623      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25624      */
25625     getDocMarkup : function(){
25626         // body styles..
25627         var st = '';
25628         
25629         // inherit styels from page...?? 
25630         if (this.stylesheets === false) {
25631             
25632             Roo.get(document.head).select('style').each(function(node) {
25633                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25634             });
25635             
25636             Roo.get(document.head).select('link').each(function(node) { 
25637                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25638             });
25639             
25640         } else if (!this.stylesheets.length) {
25641                 // simple..
25642                 st = '<style type="text/css">' +
25643                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25644                    '</style>';
25645         } else {
25646             for (var i in this.stylesheets) { 
25647                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25648             }
25649             
25650         }
25651         
25652         st +=  '<style type="text/css">' +
25653             'IMG { cursor: pointer } ' +
25654         '</style>';
25655
25656         var cls = 'roo-htmleditor-body';
25657         
25658         if(this.bodyCls.length){
25659             cls += ' ' + this.bodyCls;
25660         }
25661         
25662         return '<html><head>' + st  +
25663             //<style type="text/css">' +
25664             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25665             //'</style>' +
25666             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25667     },
25668
25669     // private
25670     onRender : function(ct, position)
25671     {
25672         var _t = this;
25673         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25674         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25675         
25676         
25677         this.el.dom.style.border = '0 none';
25678         this.el.dom.setAttribute('tabIndex', -1);
25679         this.el.addClass('x-hidden hide');
25680         
25681         
25682         
25683         if(Roo.isIE){ // fix IE 1px bogus margin
25684             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25685         }
25686        
25687         
25688         this.frameId = Roo.id();
25689         
25690          
25691         
25692         var iframe = this.owner.wrap.createChild({
25693             tag: 'iframe',
25694             cls: 'form-control', // bootstrap..
25695             id: this.frameId,
25696             name: this.frameId,
25697             frameBorder : 'no',
25698             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25699         }, this.el
25700         );
25701         
25702         
25703         this.iframe = iframe.dom;
25704
25705          this.assignDocWin();
25706         
25707         this.doc.designMode = 'on';
25708        
25709         this.doc.open();
25710         this.doc.write(this.getDocMarkup());
25711         this.doc.close();
25712
25713         
25714         var task = { // must defer to wait for browser to be ready
25715             run : function(){
25716                 //console.log("run task?" + this.doc.readyState);
25717                 this.assignDocWin();
25718                 if(this.doc.body || this.doc.readyState == 'complete'){
25719                     try {
25720                         this.doc.designMode="on";
25721                     } catch (e) {
25722                         return;
25723                     }
25724                     Roo.TaskMgr.stop(task);
25725                     this.initEditor.defer(10, this);
25726                 }
25727             },
25728             interval : 10,
25729             duration: 10000,
25730             scope: this
25731         };
25732         Roo.TaskMgr.start(task);
25733
25734     },
25735
25736     // private
25737     onResize : function(w, h)
25738     {
25739          Roo.log('resize: ' +w + ',' + h );
25740         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25741         if(!this.iframe){
25742             return;
25743         }
25744         if(typeof w == 'number'){
25745             
25746             this.iframe.style.width = w + 'px';
25747         }
25748         if(typeof h == 'number'){
25749             
25750             this.iframe.style.height = h + 'px';
25751             if(this.doc){
25752                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25753             }
25754         }
25755         
25756     },
25757
25758     /**
25759      * Toggles the editor between standard and source edit mode.
25760      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25761      */
25762     toggleSourceEdit : function(sourceEditMode){
25763         
25764         this.sourceEditMode = sourceEditMode === true;
25765         
25766         if(this.sourceEditMode){
25767  
25768             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25769             
25770         }else{
25771             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25772             //this.iframe.className = '';
25773             this.deferFocus();
25774         }
25775         //this.setSize(this.owner.wrap.getSize());
25776         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25777     },
25778
25779     
25780   
25781
25782     /**
25783      * Protected method that will not generally be called directly. If you need/want
25784      * custom HTML cleanup, this is the method you should override.
25785      * @param {String} html The HTML to be cleaned
25786      * return {String} The cleaned HTML
25787      */
25788     cleanHtml : function(html){
25789         html = String(html);
25790         if(html.length > 5){
25791             if(Roo.isSafari){ // strip safari nonsense
25792                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25793             }
25794         }
25795         if(html == '&nbsp;'){
25796             html = '';
25797         }
25798         return html;
25799     },
25800
25801     /**
25802      * HTML Editor -> Textarea
25803      * Protected method that will not generally be called directly. Syncs the contents
25804      * of the editor iframe with the textarea.
25805      */
25806     syncValue : function(){
25807         if(this.initialized){
25808             var bd = (this.doc.body || this.doc.documentElement);
25809             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25810             var html = bd.innerHTML;
25811             if(Roo.isSafari){
25812                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25813                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25814                 if(m && m[1]){
25815                     html = '<div style="'+m[0]+'">' + html + '</div>';
25816                 }
25817             }
25818             html = this.cleanHtml(html);
25819             // fix up the special chars.. normaly like back quotes in word...
25820             // however we do not want to do this with chinese..
25821             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25822                 
25823                 var cc = match.charCodeAt();
25824
25825                 // Get the character value, handling surrogate pairs
25826                 if (match.length == 2) {
25827                     // It's a surrogate pair, calculate the Unicode code point
25828                     var high = match.charCodeAt(0) - 0xD800;
25829                     var low  = match.charCodeAt(1) - 0xDC00;
25830                     cc = (high * 0x400) + low + 0x10000;
25831                 }  else if (
25832                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25833                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25834                     (cc >= 0xf900 && cc < 0xfb00 )
25835                 ) {
25836                         return match;
25837                 }  
25838          
25839                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25840                 return "&#" + cc + ";";
25841                 
25842                 
25843             });
25844             
25845             
25846              
25847             if(this.owner.fireEvent('beforesync', this, html) !== false){
25848                 this.el.dom.value = html;
25849                 this.owner.fireEvent('sync', this, html);
25850             }
25851         }
25852     },
25853
25854     /**
25855      * Protected method that will not generally be called directly. Pushes the value of the textarea
25856      * into the iframe editor.
25857      */
25858     pushValue : function(){
25859         if(this.initialized){
25860             var v = this.el.dom.value.trim();
25861             
25862 //            if(v.length < 1){
25863 //                v = '&#160;';
25864 //            }
25865             
25866             if(this.owner.fireEvent('beforepush', this, v) !== false){
25867                 var d = (this.doc.body || this.doc.documentElement);
25868                 d.innerHTML = v;
25869                 this.cleanUpPaste();
25870                 this.el.dom.value = d.innerHTML;
25871                 this.owner.fireEvent('push', this, v);
25872             }
25873         }
25874     },
25875
25876     // private
25877     deferFocus : function(){
25878         this.focus.defer(10, this);
25879     },
25880
25881     // doc'ed in Field
25882     focus : function(){
25883         if(this.win && !this.sourceEditMode){
25884             this.win.focus();
25885         }else{
25886             this.el.focus();
25887         }
25888     },
25889     
25890     assignDocWin: function()
25891     {
25892         var iframe = this.iframe;
25893         
25894          if(Roo.isIE){
25895             this.doc = iframe.contentWindow.document;
25896             this.win = iframe.contentWindow;
25897         } else {
25898 //            if (!Roo.get(this.frameId)) {
25899 //                return;
25900 //            }
25901 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25902 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25903             
25904             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25905                 return;
25906             }
25907             
25908             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25909             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25910         }
25911     },
25912     
25913     // private
25914     initEditor : function(){
25915         //console.log("INIT EDITOR");
25916         this.assignDocWin();
25917         
25918         
25919         
25920         this.doc.designMode="on";
25921         this.doc.open();
25922         this.doc.write(this.getDocMarkup());
25923         this.doc.close();
25924         
25925         var dbody = (this.doc.body || this.doc.documentElement);
25926         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25927         // this copies styles from the containing element into thsi one..
25928         // not sure why we need all of this..
25929         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25930         
25931         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25932         //ss['background-attachment'] = 'fixed'; // w3c
25933         dbody.bgProperties = 'fixed'; // ie
25934         //Roo.DomHelper.applyStyles(dbody, ss);
25935         Roo.EventManager.on(this.doc, {
25936             //'mousedown': this.onEditorEvent,
25937             'mouseup': this.onEditorEvent,
25938             'dblclick': this.onEditorEvent,
25939             'click': this.onEditorEvent,
25940             'keyup': this.onEditorEvent,
25941             buffer:100,
25942             scope: this
25943         });
25944         if(Roo.isGecko){
25945             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25946         }
25947         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25948             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25949         }
25950         this.initialized = true;
25951
25952         this.owner.fireEvent('initialize', this);
25953         this.pushValue();
25954     },
25955
25956     // private
25957     onDestroy : function(){
25958         
25959         
25960         
25961         if(this.rendered){
25962             
25963             //for (var i =0; i < this.toolbars.length;i++) {
25964             //    // fixme - ask toolbars for heights?
25965             //    this.toolbars[i].onDestroy();
25966            // }
25967             
25968             //this.wrap.dom.innerHTML = '';
25969             //this.wrap.remove();
25970         }
25971     },
25972
25973     // private
25974     onFirstFocus : function(){
25975         
25976         this.assignDocWin();
25977         
25978         
25979         this.activated = true;
25980          
25981     
25982         if(Roo.isGecko){ // prevent silly gecko errors
25983             this.win.focus();
25984             var s = this.win.getSelection();
25985             if(!s.focusNode || s.focusNode.nodeType != 3){
25986                 var r = s.getRangeAt(0);
25987                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25988                 r.collapse(true);
25989                 this.deferFocus();
25990             }
25991             try{
25992                 this.execCmd('useCSS', true);
25993                 this.execCmd('styleWithCSS', false);
25994             }catch(e){}
25995         }
25996         this.owner.fireEvent('activate', this);
25997     },
25998
25999     // private
26000     adjustFont: function(btn){
26001         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26002         //if(Roo.isSafari){ // safari
26003         //    adjust *= 2;
26004        // }
26005         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26006         if(Roo.isSafari){ // safari
26007             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26008             v =  (v < 10) ? 10 : v;
26009             v =  (v > 48) ? 48 : v;
26010             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26011             
26012         }
26013         
26014         
26015         v = Math.max(1, v+adjust);
26016         
26017         this.execCmd('FontSize', v  );
26018     },
26019
26020     onEditorEvent : function(e)
26021     {
26022         this.owner.fireEvent('editorevent', this, e);
26023       //  this.updateToolbar();
26024         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26025     },
26026
26027     insertTag : function(tg)
26028     {
26029         // could be a bit smarter... -> wrap the current selected tRoo..
26030         if (tg.toLowerCase() == 'span' ||
26031             tg.toLowerCase() == 'code' ||
26032             tg.toLowerCase() == 'sup' ||
26033             tg.toLowerCase() == 'sub' 
26034             ) {
26035             
26036             range = this.createRange(this.getSelection());
26037             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26038             wrappingNode.appendChild(range.extractContents());
26039             range.insertNode(wrappingNode);
26040
26041             return;
26042             
26043             
26044             
26045         }
26046         this.execCmd("formatblock",   tg);
26047         
26048     },
26049     
26050     insertText : function(txt)
26051     {
26052         
26053         
26054         var range = this.createRange();
26055         range.deleteContents();
26056                //alert(Sender.getAttribute('label'));
26057                
26058         range.insertNode(this.doc.createTextNode(txt));
26059     } ,
26060     
26061      
26062
26063     /**
26064      * Executes a Midas editor command on the editor document and performs necessary focus and
26065      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26066      * @param {String} cmd The Midas command
26067      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26068      */
26069     relayCmd : function(cmd, value){
26070         this.win.focus();
26071         this.execCmd(cmd, value);
26072         this.owner.fireEvent('editorevent', this);
26073         //this.updateToolbar();
26074         this.owner.deferFocus();
26075     },
26076
26077     /**
26078      * Executes a Midas editor command directly on the editor document.
26079      * For visual commands, you should use {@link #relayCmd} instead.
26080      * <b>This should only be called after the editor is initialized.</b>
26081      * @param {String} cmd The Midas command
26082      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26083      */
26084     execCmd : function(cmd, value){
26085         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26086         this.syncValue();
26087     },
26088  
26089  
26090    
26091     /**
26092      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26093      * to insert tRoo.
26094      * @param {String} text | dom node.. 
26095      */
26096     insertAtCursor : function(text)
26097     {
26098         
26099         if(!this.activated){
26100             return;
26101         }
26102         /*
26103         if(Roo.isIE){
26104             this.win.focus();
26105             var r = this.doc.selection.createRange();
26106             if(r){
26107                 r.collapse(true);
26108                 r.pasteHTML(text);
26109                 this.syncValue();
26110                 this.deferFocus();
26111             
26112             }
26113             return;
26114         }
26115         */
26116         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26117             this.win.focus();
26118             
26119             
26120             // from jquery ui (MIT licenced)
26121             var range, node;
26122             var win = this.win;
26123             
26124             if (win.getSelection && win.getSelection().getRangeAt) {
26125                 range = win.getSelection().getRangeAt(0);
26126                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26127                 range.insertNode(node);
26128             } else if (win.document.selection && win.document.selection.createRange) {
26129                 // no firefox support
26130                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26131                 win.document.selection.createRange().pasteHTML(txt);
26132             } else {
26133                 // no firefox support
26134                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26135                 this.execCmd('InsertHTML', txt);
26136             } 
26137             
26138             this.syncValue();
26139             
26140             this.deferFocus();
26141         }
26142     },
26143  // private
26144     mozKeyPress : function(e){
26145         if(e.ctrlKey){
26146             var c = e.getCharCode(), cmd;
26147           
26148             if(c > 0){
26149                 c = String.fromCharCode(c).toLowerCase();
26150                 switch(c){
26151                     case 'b':
26152                         cmd = 'bold';
26153                         break;
26154                     case 'i':
26155                         cmd = 'italic';
26156                         break;
26157                     
26158                     case 'u':
26159                         cmd = 'underline';
26160                         break;
26161                     
26162                     case 'v':
26163                         this.cleanUpPaste.defer(100, this);
26164                         return;
26165                         
26166                 }
26167                 if(cmd){
26168                     this.win.focus();
26169                     this.execCmd(cmd);
26170                     this.deferFocus();
26171                     e.preventDefault();
26172                 }
26173                 
26174             }
26175         }
26176     },
26177
26178     // private
26179     fixKeys : function(){ // load time branching for fastest keydown performance
26180         if(Roo.isIE){
26181             return function(e){
26182                 var k = e.getKey(), r;
26183                 if(k == e.TAB){
26184                     e.stopEvent();
26185                     r = this.doc.selection.createRange();
26186                     if(r){
26187                         r.collapse(true);
26188                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26189                         this.deferFocus();
26190                     }
26191                     return;
26192                 }
26193                 
26194                 if(k == e.ENTER){
26195                     r = this.doc.selection.createRange();
26196                     if(r){
26197                         var target = r.parentElement();
26198                         if(!target || target.tagName.toLowerCase() != 'li'){
26199                             e.stopEvent();
26200                             r.pasteHTML('<br />');
26201                             r.collapse(false);
26202                             r.select();
26203                         }
26204                     }
26205                 }
26206                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26207                     this.cleanUpPaste.defer(100, this);
26208                     return;
26209                 }
26210                 
26211                 
26212             };
26213         }else if(Roo.isOpera){
26214             return function(e){
26215                 var k = e.getKey();
26216                 if(k == e.TAB){
26217                     e.stopEvent();
26218                     this.win.focus();
26219                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26220                     this.deferFocus();
26221                 }
26222                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26223                     this.cleanUpPaste.defer(100, this);
26224                     return;
26225                 }
26226                 
26227             };
26228         }else if(Roo.isSafari){
26229             return function(e){
26230                 var k = e.getKey();
26231                 
26232                 if(k == e.TAB){
26233                     e.stopEvent();
26234                     this.execCmd('InsertText','\t');
26235                     this.deferFocus();
26236                     return;
26237                 }
26238                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26239                     this.cleanUpPaste.defer(100, this);
26240                     return;
26241                 }
26242                 
26243              };
26244         }
26245     }(),
26246     
26247     getAllAncestors: function()
26248     {
26249         var p = this.getSelectedNode();
26250         var a = [];
26251         if (!p) {
26252             a.push(p); // push blank onto stack..
26253             p = this.getParentElement();
26254         }
26255         
26256         
26257         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26258             a.push(p);
26259             p = p.parentNode;
26260         }
26261         a.push(this.doc.body);
26262         return a;
26263     },
26264     lastSel : false,
26265     lastSelNode : false,
26266     
26267     
26268     getSelection : function() 
26269     {
26270         this.assignDocWin();
26271         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26272     },
26273     
26274     getSelectedNode: function() 
26275     {
26276         // this may only work on Gecko!!!
26277         
26278         // should we cache this!!!!
26279         
26280         
26281         
26282          
26283         var range = this.createRange(this.getSelection()).cloneRange();
26284         
26285         if (Roo.isIE) {
26286             var parent = range.parentElement();
26287             while (true) {
26288                 var testRange = range.duplicate();
26289                 testRange.moveToElementText(parent);
26290                 if (testRange.inRange(range)) {
26291                     break;
26292                 }
26293                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26294                     break;
26295                 }
26296                 parent = parent.parentElement;
26297             }
26298             return parent;
26299         }
26300         
26301         // is ancestor a text element.
26302         var ac =  range.commonAncestorContainer;
26303         if (ac.nodeType == 3) {
26304             ac = ac.parentNode;
26305         }
26306         
26307         var ar = ac.childNodes;
26308          
26309         var nodes = [];
26310         var other_nodes = [];
26311         var has_other_nodes = false;
26312         for (var i=0;i<ar.length;i++) {
26313             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26314                 continue;
26315             }
26316             // fullly contained node.
26317             
26318             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26319                 nodes.push(ar[i]);
26320                 continue;
26321             }
26322             
26323             // probably selected..
26324             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26325                 other_nodes.push(ar[i]);
26326                 continue;
26327             }
26328             // outer..
26329             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26330                 continue;
26331             }
26332             
26333             
26334             has_other_nodes = true;
26335         }
26336         if (!nodes.length && other_nodes.length) {
26337             nodes= other_nodes;
26338         }
26339         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26340             return false;
26341         }
26342         
26343         return nodes[0];
26344     },
26345     createRange: function(sel)
26346     {
26347         // this has strange effects when using with 
26348         // top toolbar - not sure if it's a great idea.
26349         //this.editor.contentWindow.focus();
26350         if (typeof sel != "undefined") {
26351             try {
26352                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26353             } catch(e) {
26354                 return this.doc.createRange();
26355             }
26356         } else {
26357             return this.doc.createRange();
26358         }
26359     },
26360     getParentElement: function()
26361     {
26362         
26363         this.assignDocWin();
26364         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26365         
26366         var range = this.createRange(sel);
26367          
26368         try {
26369             var p = range.commonAncestorContainer;
26370             while (p.nodeType == 3) { // text node
26371                 p = p.parentNode;
26372             }
26373             return p;
26374         } catch (e) {
26375             return null;
26376         }
26377     
26378     },
26379     /***
26380      *
26381      * Range intersection.. the hard stuff...
26382      *  '-1' = before
26383      *  '0' = hits..
26384      *  '1' = after.
26385      *         [ -- selected range --- ]
26386      *   [fail]                        [fail]
26387      *
26388      *    basically..
26389      *      if end is before start or  hits it. fail.
26390      *      if start is after end or hits it fail.
26391      *
26392      *   if either hits (but other is outside. - then it's not 
26393      *   
26394      *    
26395      **/
26396     
26397     
26398     // @see http://www.thismuchiknow.co.uk/?p=64.
26399     rangeIntersectsNode : function(range, node)
26400     {
26401         var nodeRange = node.ownerDocument.createRange();
26402         try {
26403             nodeRange.selectNode(node);
26404         } catch (e) {
26405             nodeRange.selectNodeContents(node);
26406         }
26407     
26408         var rangeStartRange = range.cloneRange();
26409         rangeStartRange.collapse(true);
26410     
26411         var rangeEndRange = range.cloneRange();
26412         rangeEndRange.collapse(false);
26413     
26414         var nodeStartRange = nodeRange.cloneRange();
26415         nodeStartRange.collapse(true);
26416     
26417         var nodeEndRange = nodeRange.cloneRange();
26418         nodeEndRange.collapse(false);
26419     
26420         return rangeStartRange.compareBoundaryPoints(
26421                  Range.START_TO_START, nodeEndRange) == -1 &&
26422                rangeEndRange.compareBoundaryPoints(
26423                  Range.START_TO_START, nodeStartRange) == 1;
26424         
26425          
26426     },
26427     rangeCompareNode : function(range, node)
26428     {
26429         var nodeRange = node.ownerDocument.createRange();
26430         try {
26431             nodeRange.selectNode(node);
26432         } catch (e) {
26433             nodeRange.selectNodeContents(node);
26434         }
26435         
26436         
26437         range.collapse(true);
26438     
26439         nodeRange.collapse(true);
26440      
26441         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26442         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26443          
26444         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26445         
26446         var nodeIsBefore   =  ss == 1;
26447         var nodeIsAfter    = ee == -1;
26448         
26449         if (nodeIsBefore && nodeIsAfter) {
26450             return 0; // outer
26451         }
26452         if (!nodeIsBefore && nodeIsAfter) {
26453             return 1; //right trailed.
26454         }
26455         
26456         if (nodeIsBefore && !nodeIsAfter) {
26457             return 2;  // left trailed.
26458         }
26459         // fully contined.
26460         return 3;
26461     },
26462
26463     // private? - in a new class?
26464     cleanUpPaste :  function()
26465     {
26466         // cleans up the whole document..
26467         Roo.log('cleanuppaste');
26468         
26469         this.cleanUpChildren(this.doc.body);
26470         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26471         if (clean != this.doc.body.innerHTML) {
26472             this.doc.body.innerHTML = clean;
26473         }
26474         
26475     },
26476     
26477     cleanWordChars : function(input) {// change the chars to hex code
26478         var he = Roo.HtmlEditorCore;
26479         
26480         var output = input;
26481         Roo.each(he.swapCodes, function(sw) { 
26482             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26483             
26484             output = output.replace(swapper, sw[1]);
26485         });
26486         
26487         return output;
26488     },
26489     
26490     
26491     cleanUpChildren : function (n)
26492     {
26493         if (!n.childNodes.length) {
26494             return;
26495         }
26496         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26497            this.cleanUpChild(n.childNodes[i]);
26498         }
26499     },
26500     
26501     
26502         
26503     
26504     cleanUpChild : function (node)
26505     {
26506         var ed = this;
26507         //console.log(node);
26508         if (node.nodeName == "#text") {
26509             // clean up silly Windows -- stuff?
26510             return; 
26511         }
26512         if (node.nodeName == "#comment") {
26513             node.parentNode.removeChild(node);
26514             // clean up silly Windows -- stuff?
26515             return; 
26516         }
26517         var lcname = node.tagName.toLowerCase();
26518         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26519         // whitelist of tags..
26520         
26521         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26522             // remove node.
26523             node.parentNode.removeChild(node);
26524             return;
26525             
26526         }
26527         
26528         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26529         
26530         // spans with no attributes - just remove them..
26531         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26532             remove_keep_children = true;
26533         }
26534         
26535         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26536         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26537         
26538         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26539         //    remove_keep_children = true;
26540         //}
26541         
26542         if (remove_keep_children) {
26543             this.cleanUpChildren(node);
26544             // inserts everything just before this node...
26545             while (node.childNodes.length) {
26546                 var cn = node.childNodes[0];
26547                 node.removeChild(cn);
26548                 node.parentNode.insertBefore(cn, node);
26549             }
26550             node.parentNode.removeChild(node);
26551             return;
26552         }
26553         
26554         if (!node.attributes || !node.attributes.length) {
26555             
26556           
26557             
26558             
26559             this.cleanUpChildren(node);
26560             return;
26561         }
26562         
26563         function cleanAttr(n,v)
26564         {
26565             
26566             if (v.match(/^\./) || v.match(/^\//)) {
26567                 return;
26568             }
26569             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26570                 return;
26571             }
26572             if (v.match(/^#/)) {
26573                 return;
26574             }
26575             if (v.match(/^\{/)) { // allow template editing.
26576                 return;
26577             }
26578 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26579             node.removeAttribute(n);
26580             
26581         }
26582         
26583         var cwhite = this.cwhite;
26584         var cblack = this.cblack;
26585             
26586         function cleanStyle(n,v)
26587         {
26588             if (v.match(/expression/)) { //XSS?? should we even bother..
26589                 node.removeAttribute(n);
26590                 return;
26591             }
26592             
26593             var parts = v.split(/;/);
26594             var clean = [];
26595             
26596             Roo.each(parts, function(p) {
26597                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26598                 if (!p.length) {
26599                     return true;
26600                 }
26601                 var l = p.split(':').shift().replace(/\s+/g,'');
26602                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26603                 
26604                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26605 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26606                     //node.removeAttribute(n);
26607                     return true;
26608                 }
26609                 //Roo.log()
26610                 // only allow 'c whitelisted system attributes'
26611                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26612 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26613                     //node.removeAttribute(n);
26614                     return true;
26615                 }
26616                 
26617                 
26618                  
26619                 
26620                 clean.push(p);
26621                 return true;
26622             });
26623             if (clean.length) { 
26624                 node.setAttribute(n, clean.join(';'));
26625             } else {
26626                 node.removeAttribute(n);
26627             }
26628             
26629         }
26630         
26631         
26632         for (var i = node.attributes.length-1; i > -1 ; i--) {
26633             var a = node.attributes[i];
26634             //console.log(a);
26635             
26636             if (a.name.toLowerCase().substr(0,2)=='on')  {
26637                 node.removeAttribute(a.name);
26638                 continue;
26639             }
26640             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26641                 node.removeAttribute(a.name);
26642                 continue;
26643             }
26644             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26645                 cleanAttr(a.name,a.value); // fixme..
26646                 continue;
26647             }
26648             if (a.name == 'style') {
26649                 cleanStyle(a.name,a.value);
26650                 continue;
26651             }
26652             /// clean up MS crap..
26653             // tecnically this should be a list of valid class'es..
26654             
26655             
26656             if (a.name == 'class') {
26657                 if (a.value.match(/^Mso/)) {
26658                     node.removeAttribute('class');
26659                 }
26660                 
26661                 if (a.value.match(/^body$/)) {
26662                     node.removeAttribute('class');
26663                 }
26664                 continue;
26665             }
26666             
26667             // style cleanup!?
26668             // class cleanup?
26669             
26670         }
26671         
26672         
26673         this.cleanUpChildren(node);
26674         
26675         
26676     },
26677     
26678     /**
26679      * Clean up MS wordisms...
26680      */
26681     cleanWord : function(node)
26682     {
26683         if (!node) {
26684             this.cleanWord(this.doc.body);
26685             return;
26686         }
26687         
26688         if(
26689                 node.nodeName == 'SPAN' &&
26690                 !node.hasAttributes() &&
26691                 node.childNodes.length == 1 &&
26692                 node.firstChild.nodeName == "#text"  
26693         ) {
26694             var textNode = node.firstChild;
26695             node.removeChild(textNode);
26696             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26697                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26698             }
26699             node.parentNode.insertBefore(textNode, node);
26700             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26701                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26702             }
26703             node.parentNode.removeChild(node);
26704         }
26705         
26706         if (node.nodeName == "#text") {
26707             // clean up silly Windows -- stuff?
26708             return; 
26709         }
26710         if (node.nodeName == "#comment") {
26711             node.parentNode.removeChild(node);
26712             // clean up silly Windows -- stuff?
26713             return; 
26714         }
26715         
26716         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26717             node.parentNode.removeChild(node);
26718             return;
26719         }
26720         //Roo.log(node.tagName);
26721         // remove - but keep children..
26722         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26723             //Roo.log('-- removed');
26724             while (node.childNodes.length) {
26725                 var cn = node.childNodes[0];
26726                 node.removeChild(cn);
26727                 node.parentNode.insertBefore(cn, node);
26728                 // move node to parent - and clean it..
26729                 this.cleanWord(cn);
26730             }
26731             node.parentNode.removeChild(node);
26732             /// no need to iterate chidlren = it's got none..
26733             //this.iterateChildren(node, this.cleanWord);
26734             return;
26735         }
26736         // clean styles
26737         if (node.className.length) {
26738             
26739             var cn = node.className.split(/\W+/);
26740             var cna = [];
26741             Roo.each(cn, function(cls) {
26742                 if (cls.match(/Mso[a-zA-Z]+/)) {
26743                     return;
26744                 }
26745                 cna.push(cls);
26746             });
26747             node.className = cna.length ? cna.join(' ') : '';
26748             if (!cna.length) {
26749                 node.removeAttribute("class");
26750             }
26751         }
26752         
26753         if (node.hasAttribute("lang")) {
26754             node.removeAttribute("lang");
26755         }
26756         
26757         if (node.hasAttribute("style")) {
26758             
26759             var styles = node.getAttribute("style").split(";");
26760             var nstyle = [];
26761             Roo.each(styles, function(s) {
26762                 if (!s.match(/:/)) {
26763                     return;
26764                 }
26765                 var kv = s.split(":");
26766                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26767                     return;
26768                 }
26769                 // what ever is left... we allow.
26770                 nstyle.push(s);
26771             });
26772             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26773             if (!nstyle.length) {
26774                 node.removeAttribute('style');
26775             }
26776         }
26777         this.iterateChildren(node, this.cleanWord);
26778         
26779         
26780         
26781     },
26782     /**
26783      * iterateChildren of a Node, calling fn each time, using this as the scole..
26784      * @param {DomNode} node node to iterate children of.
26785      * @param {Function} fn method of this class to call on each item.
26786      */
26787     iterateChildren : function(node, fn)
26788     {
26789         if (!node.childNodes.length) {
26790                 return;
26791         }
26792         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26793            fn.call(this, node.childNodes[i])
26794         }
26795     },
26796     
26797     
26798     /**
26799      * cleanTableWidths.
26800      *
26801      * Quite often pasting from word etc.. results in tables with column and widths.
26802      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26803      *
26804      */
26805     cleanTableWidths : function(node)
26806     {
26807          
26808          
26809         if (!node) {
26810             this.cleanTableWidths(this.doc.body);
26811             return;
26812         }
26813         
26814         // ignore list...
26815         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26816             return; 
26817         }
26818         Roo.log(node.tagName);
26819         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26820             this.iterateChildren(node, this.cleanTableWidths);
26821             return;
26822         }
26823         if (node.hasAttribute('width')) {
26824             node.removeAttribute('width');
26825         }
26826         
26827          
26828         if (node.hasAttribute("style")) {
26829             // pretty basic...
26830             
26831             var styles = node.getAttribute("style").split(";");
26832             var nstyle = [];
26833             Roo.each(styles, function(s) {
26834                 if (!s.match(/:/)) {
26835                     return;
26836                 }
26837                 var kv = s.split(":");
26838                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26839                     return;
26840                 }
26841                 // what ever is left... we allow.
26842                 nstyle.push(s);
26843             });
26844             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26845             if (!nstyle.length) {
26846                 node.removeAttribute('style');
26847             }
26848         }
26849         
26850         this.iterateChildren(node, this.cleanTableWidths);
26851         
26852         
26853     },
26854     
26855     
26856     
26857     
26858     domToHTML : function(currentElement, depth, nopadtext) {
26859         
26860         depth = depth || 0;
26861         nopadtext = nopadtext || false;
26862     
26863         if (!currentElement) {
26864             return this.domToHTML(this.doc.body);
26865         }
26866         
26867         //Roo.log(currentElement);
26868         var j;
26869         var allText = false;
26870         var nodeName = currentElement.nodeName;
26871         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26872         
26873         if  (nodeName == '#text') {
26874             
26875             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26876         }
26877         
26878         
26879         var ret = '';
26880         if (nodeName != 'BODY') {
26881              
26882             var i = 0;
26883             // Prints the node tagName, such as <A>, <IMG>, etc
26884             if (tagName) {
26885                 var attr = [];
26886                 for(i = 0; i < currentElement.attributes.length;i++) {
26887                     // quoting?
26888                     var aname = currentElement.attributes.item(i).name;
26889                     if (!currentElement.attributes.item(i).value.length) {
26890                         continue;
26891                     }
26892                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26893                 }
26894                 
26895                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26896             } 
26897             else {
26898                 
26899                 // eack
26900             }
26901         } else {
26902             tagName = false;
26903         }
26904         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26905             return ret;
26906         }
26907         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26908             nopadtext = true;
26909         }
26910         
26911         
26912         // Traverse the tree
26913         i = 0;
26914         var currentElementChild = currentElement.childNodes.item(i);
26915         var allText = true;
26916         var innerHTML  = '';
26917         lastnode = '';
26918         while (currentElementChild) {
26919             // Formatting code (indent the tree so it looks nice on the screen)
26920             var nopad = nopadtext;
26921             if (lastnode == 'SPAN') {
26922                 nopad  = true;
26923             }
26924             // text
26925             if  (currentElementChild.nodeName == '#text') {
26926                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26927                 toadd = nopadtext ? toadd : toadd.trim();
26928                 if (!nopad && toadd.length > 80) {
26929                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26930                 }
26931                 innerHTML  += toadd;
26932                 
26933                 i++;
26934                 currentElementChild = currentElement.childNodes.item(i);
26935                 lastNode = '';
26936                 continue;
26937             }
26938             allText = false;
26939             
26940             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26941                 
26942             // Recursively traverse the tree structure of the child node
26943             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26944             lastnode = currentElementChild.nodeName;
26945             i++;
26946             currentElementChild=currentElement.childNodes.item(i);
26947         }
26948         
26949         ret += innerHTML;
26950         
26951         if (!allText) {
26952                 // The remaining code is mostly for formatting the tree
26953             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26954         }
26955         
26956         
26957         if (tagName) {
26958             ret+= "</"+tagName+">";
26959         }
26960         return ret;
26961         
26962     },
26963         
26964     applyBlacklists : function()
26965     {
26966         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26967         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26968         
26969         this.white = [];
26970         this.black = [];
26971         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26972             if (b.indexOf(tag) > -1) {
26973                 return;
26974             }
26975             this.white.push(tag);
26976             
26977         }, this);
26978         
26979         Roo.each(w, function(tag) {
26980             if (b.indexOf(tag) > -1) {
26981                 return;
26982             }
26983             if (this.white.indexOf(tag) > -1) {
26984                 return;
26985             }
26986             this.white.push(tag);
26987             
26988         }, this);
26989         
26990         
26991         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26992             if (w.indexOf(tag) > -1) {
26993                 return;
26994             }
26995             this.black.push(tag);
26996             
26997         }, this);
26998         
26999         Roo.each(b, function(tag) {
27000             if (w.indexOf(tag) > -1) {
27001                 return;
27002             }
27003             if (this.black.indexOf(tag) > -1) {
27004                 return;
27005             }
27006             this.black.push(tag);
27007             
27008         }, this);
27009         
27010         
27011         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27012         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27013         
27014         this.cwhite = [];
27015         this.cblack = [];
27016         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27017             if (b.indexOf(tag) > -1) {
27018                 return;
27019             }
27020             this.cwhite.push(tag);
27021             
27022         }, this);
27023         
27024         Roo.each(w, function(tag) {
27025             if (b.indexOf(tag) > -1) {
27026                 return;
27027             }
27028             if (this.cwhite.indexOf(tag) > -1) {
27029                 return;
27030             }
27031             this.cwhite.push(tag);
27032             
27033         }, this);
27034         
27035         
27036         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27037             if (w.indexOf(tag) > -1) {
27038                 return;
27039             }
27040             this.cblack.push(tag);
27041             
27042         }, this);
27043         
27044         Roo.each(b, function(tag) {
27045             if (w.indexOf(tag) > -1) {
27046                 return;
27047             }
27048             if (this.cblack.indexOf(tag) > -1) {
27049                 return;
27050             }
27051             this.cblack.push(tag);
27052             
27053         }, this);
27054     },
27055     
27056     setStylesheets : function(stylesheets)
27057     {
27058         if(typeof(stylesheets) == 'string'){
27059             Roo.get(this.iframe.contentDocument.head).createChild({
27060                 tag : 'link',
27061                 rel : 'stylesheet',
27062                 type : 'text/css',
27063                 href : stylesheets
27064             });
27065             
27066             return;
27067         }
27068         var _this = this;
27069      
27070         Roo.each(stylesheets, function(s) {
27071             if(!s.length){
27072                 return;
27073             }
27074             
27075             Roo.get(_this.iframe.contentDocument.head).createChild({
27076                 tag : 'link',
27077                 rel : 'stylesheet',
27078                 type : 'text/css',
27079                 href : s
27080             });
27081         });
27082
27083         
27084     },
27085     
27086     removeStylesheets : function()
27087     {
27088         var _this = this;
27089         
27090         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27091             s.remove();
27092         });
27093     },
27094     
27095     setStyle : function(style)
27096     {
27097         Roo.get(this.iframe.contentDocument.head).createChild({
27098             tag : 'style',
27099             type : 'text/css',
27100             html : style
27101         });
27102
27103         return;
27104     }
27105     
27106     // hide stuff that is not compatible
27107     /**
27108      * @event blur
27109      * @hide
27110      */
27111     /**
27112      * @event change
27113      * @hide
27114      */
27115     /**
27116      * @event focus
27117      * @hide
27118      */
27119     /**
27120      * @event specialkey
27121      * @hide
27122      */
27123     /**
27124      * @cfg {String} fieldClass @hide
27125      */
27126     /**
27127      * @cfg {String} focusClass @hide
27128      */
27129     /**
27130      * @cfg {String} autoCreate @hide
27131      */
27132     /**
27133      * @cfg {String} inputType @hide
27134      */
27135     /**
27136      * @cfg {String} invalidClass @hide
27137      */
27138     /**
27139      * @cfg {String} invalidText @hide
27140      */
27141     /**
27142      * @cfg {String} msgFx @hide
27143      */
27144     /**
27145      * @cfg {String} validateOnBlur @hide
27146      */
27147 });
27148
27149 Roo.HtmlEditorCore.white = [
27150         'area', 'br', 'img', 'input', 'hr', 'wbr',
27151         
27152        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27153        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27154        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27155        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27156        'table',   'ul',         'xmp', 
27157        
27158        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27159       'thead',   'tr', 
27160      
27161       'dir', 'menu', 'ol', 'ul', 'dl',
27162        
27163       'embed',  'object'
27164 ];
27165
27166
27167 Roo.HtmlEditorCore.black = [
27168     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27169         'applet', // 
27170         'base',   'basefont', 'bgsound', 'blink',  'body', 
27171         'frame',  'frameset', 'head',    'html',   'ilayer', 
27172         'iframe', 'layer',  'link',     'meta',    'object',   
27173         'script', 'style' ,'title',  'xml' // clean later..
27174 ];
27175 Roo.HtmlEditorCore.clean = [
27176     'script', 'style', 'title', 'xml'
27177 ];
27178 Roo.HtmlEditorCore.remove = [
27179     'font'
27180 ];
27181 // attributes..
27182
27183 Roo.HtmlEditorCore.ablack = [
27184     'on'
27185 ];
27186     
27187 Roo.HtmlEditorCore.aclean = [ 
27188     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27189 ];
27190
27191 // protocols..
27192 Roo.HtmlEditorCore.pwhite= [
27193         'http',  'https',  'mailto'
27194 ];
27195
27196 // white listed style attributes.
27197 Roo.HtmlEditorCore.cwhite= [
27198       //  'text-align', /// default is to allow most things..
27199       
27200          
27201 //        'font-size'//??
27202 ];
27203
27204 // black listed style attributes.
27205 Roo.HtmlEditorCore.cblack= [
27206       //  'font-size' -- this can be set by the project 
27207 ];
27208
27209
27210 Roo.HtmlEditorCore.swapCodes   =[ 
27211     [    8211, "&#8211;" ], 
27212     [    8212, "&#8212;" ], 
27213     [    8216,  "'" ],  
27214     [    8217, "'" ],  
27215     [    8220, '"' ],  
27216     [    8221, '"' ],  
27217     [    8226, "*" ],  
27218     [    8230, "..." ]
27219 ]; 
27220
27221     /*
27222  * - LGPL
27223  *
27224  * HtmlEditor
27225  * 
27226  */
27227
27228 /**
27229  * @class Roo.bootstrap.HtmlEditor
27230  * @extends Roo.bootstrap.TextArea
27231  * Bootstrap HtmlEditor class
27232
27233  * @constructor
27234  * Create a new HtmlEditor
27235  * @param {Object} config The config object
27236  */
27237
27238 Roo.bootstrap.HtmlEditor = function(config){
27239     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27240     if (!this.toolbars) {
27241         this.toolbars = [];
27242     }
27243     
27244     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27245     this.addEvents({
27246             /**
27247              * @event initialize
27248              * Fires when the editor is fully initialized (including the iframe)
27249              * @param {HtmlEditor} this
27250              */
27251             initialize: true,
27252             /**
27253              * @event activate
27254              * Fires when the editor is first receives the focus. Any insertion must wait
27255              * until after this event.
27256              * @param {HtmlEditor} this
27257              */
27258             activate: true,
27259              /**
27260              * @event beforesync
27261              * Fires before the textarea is updated with content from the editor iframe. Return false
27262              * to cancel the sync.
27263              * @param {HtmlEditor} this
27264              * @param {String} html
27265              */
27266             beforesync: true,
27267              /**
27268              * @event beforepush
27269              * Fires before the iframe editor is updated with content from the textarea. Return false
27270              * to cancel the push.
27271              * @param {HtmlEditor} this
27272              * @param {String} html
27273              */
27274             beforepush: true,
27275              /**
27276              * @event sync
27277              * Fires when the textarea is updated with content from the editor iframe.
27278              * @param {HtmlEditor} this
27279              * @param {String} html
27280              */
27281             sync: true,
27282              /**
27283              * @event push
27284              * Fires when the iframe editor is updated with content from the textarea.
27285              * @param {HtmlEditor} this
27286              * @param {String} html
27287              */
27288             push: true,
27289              /**
27290              * @event editmodechange
27291              * Fires when the editor switches edit modes
27292              * @param {HtmlEditor} this
27293              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27294              */
27295             editmodechange: true,
27296             /**
27297              * @event editorevent
27298              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27299              * @param {HtmlEditor} this
27300              */
27301             editorevent: true,
27302             /**
27303              * @event firstfocus
27304              * Fires when on first focus - needed by toolbars..
27305              * @param {HtmlEditor} this
27306              */
27307             firstfocus: true,
27308             /**
27309              * @event autosave
27310              * Auto save the htmlEditor value as a file into Events
27311              * @param {HtmlEditor} this
27312              */
27313             autosave: true,
27314             /**
27315              * @event savedpreview
27316              * preview the saved version of htmlEditor
27317              * @param {HtmlEditor} this
27318              */
27319             savedpreview: true
27320         });
27321 };
27322
27323
27324 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27325     
27326     
27327       /**
27328      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27329      */
27330     toolbars : false,
27331     
27332      /**
27333     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27334     */
27335     btns : [],
27336    
27337      /**
27338      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27339      *                        Roo.resizable.
27340      */
27341     resizable : false,
27342      /**
27343      * @cfg {Number} height (in pixels)
27344      */   
27345     height: 300,
27346    /**
27347      * @cfg {Number} width (in pixels)
27348      */   
27349     width: false,
27350     
27351     /**
27352      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27353      * 
27354      */
27355     stylesheets: false,
27356     
27357     // id of frame..
27358     frameId: false,
27359     
27360     // private properties
27361     validationEvent : false,
27362     deferHeight: true,
27363     initialized : false,
27364     activated : false,
27365     
27366     onFocus : Roo.emptyFn,
27367     iframePad:3,
27368     hideMode:'offsets',
27369     
27370     tbContainer : false,
27371     
27372     bodyCls : '',
27373     
27374     toolbarContainer :function() {
27375         return this.wrap.select('.x-html-editor-tb',true).first();
27376     },
27377
27378     /**
27379      * Protected method that will not generally be called directly. It
27380      * is called when the editor creates its toolbar. Override this method if you need to
27381      * add custom toolbar buttons.
27382      * @param {HtmlEditor} editor
27383      */
27384     createToolbar : function(){
27385         Roo.log('renewing');
27386         Roo.log("create toolbars");
27387         
27388         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27389         this.toolbars[0].render(this.toolbarContainer());
27390         
27391         return;
27392         
27393 //        if (!editor.toolbars || !editor.toolbars.length) {
27394 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27395 //        }
27396 //        
27397 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27398 //            editor.toolbars[i] = Roo.factory(
27399 //                    typeof(editor.toolbars[i]) == 'string' ?
27400 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27401 //                Roo.bootstrap.HtmlEditor);
27402 //            editor.toolbars[i].init(editor);
27403 //        }
27404     },
27405
27406      
27407     // private
27408     onRender : function(ct, position)
27409     {
27410        // Roo.log("Call onRender: " + this.xtype);
27411         var _t = this;
27412         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27413       
27414         this.wrap = this.inputEl().wrap({
27415             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27416         });
27417         
27418         this.editorcore.onRender(ct, position);
27419          
27420         if (this.resizable) {
27421             this.resizeEl = new Roo.Resizable(this.wrap, {
27422                 pinned : true,
27423                 wrap: true,
27424                 dynamic : true,
27425                 minHeight : this.height,
27426                 height: this.height,
27427                 handles : this.resizable,
27428                 width: this.width,
27429                 listeners : {
27430                     resize : function(r, w, h) {
27431                         _t.onResize(w,h); // -something
27432                     }
27433                 }
27434             });
27435             
27436         }
27437         this.createToolbar(this);
27438        
27439         
27440         if(!this.width && this.resizable){
27441             this.setSize(this.wrap.getSize());
27442         }
27443         if (this.resizeEl) {
27444             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27445             // should trigger onReize..
27446         }
27447         
27448     },
27449
27450     // private
27451     onResize : function(w, h)
27452     {
27453         Roo.log('resize: ' +w + ',' + h );
27454         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27455         var ew = false;
27456         var eh = false;
27457         
27458         if(this.inputEl() ){
27459             if(typeof w == 'number'){
27460                 var aw = w - this.wrap.getFrameWidth('lr');
27461                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27462                 ew = aw;
27463             }
27464             if(typeof h == 'number'){
27465                  var tbh = -11;  // fixme it needs to tool bar size!
27466                 for (var i =0; i < this.toolbars.length;i++) {
27467                     // fixme - ask toolbars for heights?
27468                     tbh += this.toolbars[i].el.getHeight();
27469                     //if (this.toolbars[i].footer) {
27470                     //    tbh += this.toolbars[i].footer.el.getHeight();
27471                     //}
27472                 }
27473               
27474                 
27475                 
27476                 
27477                 
27478                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27479                 ah -= 5; // knock a few pixes off for look..
27480                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27481                 var eh = ah;
27482             }
27483         }
27484         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27485         this.editorcore.onResize(ew,eh);
27486         
27487     },
27488
27489     /**
27490      * Toggles the editor between standard and source edit mode.
27491      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27492      */
27493     toggleSourceEdit : function(sourceEditMode)
27494     {
27495         this.editorcore.toggleSourceEdit(sourceEditMode);
27496         
27497         if(this.editorcore.sourceEditMode){
27498             Roo.log('editor - showing textarea');
27499             
27500 //            Roo.log('in');
27501 //            Roo.log(this.syncValue());
27502             this.syncValue();
27503             this.inputEl().removeClass(['hide', 'x-hidden']);
27504             this.inputEl().dom.removeAttribute('tabIndex');
27505             this.inputEl().focus();
27506         }else{
27507             Roo.log('editor - hiding textarea');
27508 //            Roo.log('out')
27509 //            Roo.log(this.pushValue()); 
27510             this.pushValue();
27511             
27512             this.inputEl().addClass(['hide', 'x-hidden']);
27513             this.inputEl().dom.setAttribute('tabIndex', -1);
27514             //this.deferFocus();
27515         }
27516          
27517         if(this.resizable){
27518             this.setSize(this.wrap.getSize());
27519         }
27520         
27521         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27522     },
27523  
27524     // private (for BoxComponent)
27525     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27526
27527     // private (for BoxComponent)
27528     getResizeEl : function(){
27529         return this.wrap;
27530     },
27531
27532     // private (for BoxComponent)
27533     getPositionEl : function(){
27534         return this.wrap;
27535     },
27536
27537     // private
27538     initEvents : function(){
27539         this.originalValue = this.getValue();
27540     },
27541
27542 //    /**
27543 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27544 //     * @method
27545 //     */
27546 //    markInvalid : Roo.emptyFn,
27547 //    /**
27548 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27549 //     * @method
27550 //     */
27551 //    clearInvalid : Roo.emptyFn,
27552
27553     setValue : function(v){
27554         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27555         this.editorcore.pushValue();
27556     },
27557
27558      
27559     // private
27560     deferFocus : function(){
27561         this.focus.defer(10, this);
27562     },
27563
27564     // doc'ed in Field
27565     focus : function(){
27566         this.editorcore.focus();
27567         
27568     },
27569       
27570
27571     // private
27572     onDestroy : function(){
27573         
27574         
27575         
27576         if(this.rendered){
27577             
27578             for (var i =0; i < this.toolbars.length;i++) {
27579                 // fixme - ask toolbars for heights?
27580                 this.toolbars[i].onDestroy();
27581             }
27582             
27583             this.wrap.dom.innerHTML = '';
27584             this.wrap.remove();
27585         }
27586     },
27587
27588     // private
27589     onFirstFocus : function(){
27590         //Roo.log("onFirstFocus");
27591         this.editorcore.onFirstFocus();
27592          for (var i =0; i < this.toolbars.length;i++) {
27593             this.toolbars[i].onFirstFocus();
27594         }
27595         
27596     },
27597     
27598     // private
27599     syncValue : function()
27600     {   
27601         this.editorcore.syncValue();
27602     },
27603     
27604     pushValue : function()
27605     {   
27606         this.editorcore.pushValue();
27607     }
27608      
27609     
27610     // hide stuff that is not compatible
27611     /**
27612      * @event blur
27613      * @hide
27614      */
27615     /**
27616      * @event change
27617      * @hide
27618      */
27619     /**
27620      * @event focus
27621      * @hide
27622      */
27623     /**
27624      * @event specialkey
27625      * @hide
27626      */
27627     /**
27628      * @cfg {String} fieldClass @hide
27629      */
27630     /**
27631      * @cfg {String} focusClass @hide
27632      */
27633     /**
27634      * @cfg {String} autoCreate @hide
27635      */
27636     /**
27637      * @cfg {String} inputType @hide
27638      */
27639      
27640     /**
27641      * @cfg {String} invalidText @hide
27642      */
27643     /**
27644      * @cfg {String} msgFx @hide
27645      */
27646     /**
27647      * @cfg {String} validateOnBlur @hide
27648      */
27649 });
27650  
27651     
27652    
27653    
27654    
27655       
27656 Roo.namespace('Roo.bootstrap.htmleditor');
27657 /**
27658  * @class Roo.bootstrap.HtmlEditorToolbar1
27659  * Basic Toolbar
27660  * 
27661  * @example
27662  * Usage:
27663  *
27664  new Roo.bootstrap.HtmlEditor({
27665     ....
27666     toolbars : [
27667         new Roo.bootstrap.HtmlEditorToolbar1({
27668             disable : { fonts: 1 , format: 1, ..., ... , ...],
27669             btns : [ .... ]
27670         })
27671     }
27672      
27673  * 
27674  * @cfg {Object} disable List of elements to disable..
27675  * @cfg {Array} btns List of additional buttons.
27676  * 
27677  * 
27678  * NEEDS Extra CSS? 
27679  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27680  */
27681  
27682 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27683 {
27684     
27685     Roo.apply(this, config);
27686     
27687     // default disabled, based on 'good practice'..
27688     this.disable = this.disable || {};
27689     Roo.applyIf(this.disable, {
27690         fontSize : true,
27691         colors : true,
27692         specialElements : true
27693     });
27694     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27695     
27696     this.editor = config.editor;
27697     this.editorcore = config.editor.editorcore;
27698     
27699     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27700     
27701     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27702     // dont call parent... till later.
27703 }
27704 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27705      
27706     bar : true,
27707     
27708     editor : false,
27709     editorcore : false,
27710     
27711     
27712     formats : [
27713         "p" ,  
27714         "h1","h2","h3","h4","h5","h6", 
27715         "pre", "code", 
27716         "abbr", "acronym", "address", "cite", "samp", "var",
27717         'div','span'
27718     ],
27719     
27720     onRender : function(ct, position)
27721     {
27722        // Roo.log("Call onRender: " + this.xtype);
27723         
27724        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27725        Roo.log(this.el);
27726        this.el.dom.style.marginBottom = '0';
27727        var _this = this;
27728        var editorcore = this.editorcore;
27729        var editor= this.editor;
27730        
27731        var children = [];
27732        var btn = function(id,cmd , toggle, handler, html){
27733        
27734             var  event = toggle ? 'toggle' : 'click';
27735        
27736             var a = {
27737                 size : 'sm',
27738                 xtype: 'Button',
27739                 xns: Roo.bootstrap,
27740                 //glyphicon : id,
27741                 fa: id,
27742                 cmd : id || cmd,
27743                 enableToggle:toggle !== false,
27744                 html : html || '',
27745                 pressed : toggle ? false : null,
27746                 listeners : {}
27747             };
27748             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27749                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27750             };
27751             children.push(a);
27752             return a;
27753        }
27754        
27755     //    var cb_box = function...
27756         
27757         var style = {
27758                 xtype: 'Button',
27759                 size : 'sm',
27760                 xns: Roo.bootstrap,
27761                 fa : 'font',
27762                 //html : 'submit'
27763                 menu : {
27764                     xtype: 'Menu',
27765                     xns: Roo.bootstrap,
27766                     items:  []
27767                 }
27768         };
27769         Roo.each(this.formats, function(f) {
27770             style.menu.items.push({
27771                 xtype :'MenuItem',
27772                 xns: Roo.bootstrap,
27773                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27774                 tagname : f,
27775                 listeners : {
27776                     click : function()
27777                     {
27778                         editorcore.insertTag(this.tagname);
27779                         editor.focus();
27780                     }
27781                 }
27782                 
27783             });
27784         });
27785         children.push(style);   
27786         
27787         btn('bold',false,true);
27788         btn('italic',false,true);
27789         btn('align-left', 'justifyleft',true);
27790         btn('align-center', 'justifycenter',true);
27791         btn('align-right' , 'justifyright',true);
27792         btn('link', false, false, function(btn) {
27793             //Roo.log("create link?");
27794             var url = prompt(this.createLinkText, this.defaultLinkValue);
27795             if(url && url != 'http:/'+'/'){
27796                 this.editorcore.relayCmd('createlink', url);
27797             }
27798         }),
27799         btn('list','insertunorderedlist',true);
27800         btn('pencil', false,true, function(btn){
27801                 Roo.log(this);
27802                 this.toggleSourceEdit(btn.pressed);
27803         });
27804         
27805         if (this.editor.btns.length > 0) {
27806             for (var i = 0; i<this.editor.btns.length; i++) {
27807                 children.push(this.editor.btns[i]);
27808             }
27809         }
27810         
27811         /*
27812         var cog = {
27813                 xtype: 'Button',
27814                 size : 'sm',
27815                 xns: Roo.bootstrap,
27816                 glyphicon : 'cog',
27817                 //html : 'submit'
27818                 menu : {
27819                     xtype: 'Menu',
27820                     xns: Roo.bootstrap,
27821                     items:  []
27822                 }
27823         };
27824         
27825         cog.menu.items.push({
27826             xtype :'MenuItem',
27827             xns: Roo.bootstrap,
27828             html : Clean styles,
27829             tagname : f,
27830             listeners : {
27831                 click : function()
27832                 {
27833                     editorcore.insertTag(this.tagname);
27834                     editor.focus();
27835                 }
27836             }
27837             
27838         });
27839        */
27840         
27841          
27842        this.xtype = 'NavSimplebar';
27843         
27844         for(var i=0;i< children.length;i++) {
27845             
27846             this.buttons.add(this.addxtypeChild(children[i]));
27847             
27848         }
27849         
27850         editor.on('editorevent', this.updateToolbar, this);
27851     },
27852     onBtnClick : function(id)
27853     {
27854        this.editorcore.relayCmd(id);
27855        this.editorcore.focus();
27856     },
27857     
27858     /**
27859      * Protected method that will not generally be called directly. It triggers
27860      * a toolbar update by reading the markup state of the current selection in the editor.
27861      */
27862     updateToolbar: function(){
27863
27864         if(!this.editorcore.activated){
27865             this.editor.onFirstFocus(); // is this neeed?
27866             return;
27867         }
27868
27869         var btns = this.buttons; 
27870         var doc = this.editorcore.doc;
27871         btns.get('bold').setActive(doc.queryCommandState('bold'));
27872         btns.get('italic').setActive(doc.queryCommandState('italic'));
27873         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27874         
27875         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27876         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27877         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27878         
27879         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27880         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27881          /*
27882         
27883         var ans = this.editorcore.getAllAncestors();
27884         if (this.formatCombo) {
27885             
27886             
27887             var store = this.formatCombo.store;
27888             this.formatCombo.setValue("");
27889             for (var i =0; i < ans.length;i++) {
27890                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27891                     // select it..
27892                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27893                     break;
27894                 }
27895             }
27896         }
27897         
27898         
27899         
27900         // hides menus... - so this cant be on a menu...
27901         Roo.bootstrap.MenuMgr.hideAll();
27902         */
27903         Roo.bootstrap.MenuMgr.hideAll();
27904         //this.editorsyncValue();
27905     },
27906     onFirstFocus: function() {
27907         this.buttons.each(function(item){
27908            item.enable();
27909         });
27910     },
27911     toggleSourceEdit : function(sourceEditMode){
27912         
27913           
27914         if(sourceEditMode){
27915             Roo.log("disabling buttons");
27916            this.buttons.each( function(item){
27917                 if(item.cmd != 'pencil'){
27918                     item.disable();
27919                 }
27920             });
27921           
27922         }else{
27923             Roo.log("enabling buttons");
27924             if(this.editorcore.initialized){
27925                 this.buttons.each( function(item){
27926                     item.enable();
27927                 });
27928             }
27929             
27930         }
27931         Roo.log("calling toggole on editor");
27932         // tell the editor that it's been pressed..
27933         this.editor.toggleSourceEdit(sourceEditMode);
27934        
27935     }
27936 });
27937
27938
27939
27940
27941  
27942 /*
27943  * - LGPL
27944  */
27945
27946 /**
27947  * @class Roo.bootstrap.Markdown
27948  * @extends Roo.bootstrap.TextArea
27949  * Bootstrap Showdown editable area
27950  * @cfg {string} content
27951  * 
27952  * @constructor
27953  * Create a new Showdown
27954  */
27955
27956 Roo.bootstrap.Markdown = function(config){
27957     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27958    
27959 };
27960
27961 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27962     
27963     editing :false,
27964     
27965     initEvents : function()
27966     {
27967         
27968         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27969         this.markdownEl = this.el.createChild({
27970             cls : 'roo-markdown-area'
27971         });
27972         this.inputEl().addClass('d-none');
27973         if (this.getValue() == '') {
27974             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27975             
27976         } else {
27977             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27978         }
27979         this.markdownEl.on('click', this.toggleTextEdit, this);
27980         this.on('blur', this.toggleTextEdit, this);
27981         this.on('specialkey', this.resizeTextArea, this);
27982     },
27983     
27984     toggleTextEdit : function()
27985     {
27986         var sh = this.markdownEl.getHeight();
27987         this.inputEl().addClass('d-none');
27988         this.markdownEl.addClass('d-none');
27989         if (!this.editing) {
27990             // show editor?
27991             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27992             this.inputEl().removeClass('d-none');
27993             this.inputEl().focus();
27994             this.editing = true;
27995             return;
27996         }
27997         // show showdown...
27998         this.updateMarkdown();
27999         this.markdownEl.removeClass('d-none');
28000         this.editing = false;
28001         return;
28002     },
28003     updateMarkdown : function()
28004     {
28005         if (this.getValue() == '') {
28006             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28007             return;
28008         }
28009  
28010         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28011     },
28012     
28013     resizeTextArea: function () {
28014         
28015         var sh = 100;
28016         Roo.log([sh, this.getValue().split("\n").length * 30]);
28017         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28018     },
28019     setValue : function(val)
28020     {
28021         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28022         if (!this.editing) {
28023             this.updateMarkdown();
28024         }
28025         
28026     },
28027     focus : function()
28028     {
28029         if (!this.editing) {
28030             this.toggleTextEdit();
28031         }
28032         
28033     }
28034
28035
28036 });/*
28037  * Based on:
28038  * Ext JS Library 1.1.1
28039  * Copyright(c) 2006-2007, Ext JS, LLC.
28040  *
28041  * Originally Released Under LGPL - original licence link has changed is not relivant.
28042  *
28043  * Fork - LGPL
28044  * <script type="text/javascript">
28045  */
28046  
28047 /**
28048  * @class Roo.bootstrap.PagingToolbar
28049  * @extends Roo.bootstrap.NavSimplebar
28050  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28051  * @constructor
28052  * Create a new PagingToolbar
28053  * @param {Object} config The config object
28054  * @param {Roo.data.Store} store
28055  */
28056 Roo.bootstrap.PagingToolbar = function(config)
28057 {
28058     // old args format still supported... - xtype is prefered..
28059         // created from xtype...
28060     
28061     this.ds = config.dataSource;
28062     
28063     if (config.store && !this.ds) {
28064         this.store= Roo.factory(config.store, Roo.data);
28065         this.ds = this.store;
28066         this.ds.xmodule = this.xmodule || false;
28067     }
28068     
28069     this.toolbarItems = [];
28070     if (config.items) {
28071         this.toolbarItems = config.items;
28072     }
28073     
28074     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28075     
28076     this.cursor = 0;
28077     
28078     if (this.ds) { 
28079         this.bind(this.ds);
28080     }
28081     
28082     if (Roo.bootstrap.version == 4) {
28083         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28084     } else {
28085         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28086     }
28087     
28088 };
28089
28090 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28091     /**
28092      * @cfg {Roo.data.Store} dataSource
28093      * The underlying data store providing the paged data
28094      */
28095     /**
28096      * @cfg {String/HTMLElement/Element} container
28097      * container The id or element that will contain the toolbar
28098      */
28099     /**
28100      * @cfg {Boolean} displayInfo
28101      * True to display the displayMsg (defaults to false)
28102      */
28103     /**
28104      * @cfg {Number} pageSize
28105      * The number of records to display per page (defaults to 20)
28106      */
28107     pageSize: 20,
28108     /**
28109      * @cfg {String} displayMsg
28110      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28111      */
28112     displayMsg : 'Displaying {0} - {1} of {2}',
28113     /**
28114      * @cfg {String} emptyMsg
28115      * The message to display when no records are found (defaults to "No data to display")
28116      */
28117     emptyMsg : 'No data to display',
28118     /**
28119      * Customizable piece of the default paging text (defaults to "Page")
28120      * @type String
28121      */
28122     beforePageText : "Page",
28123     /**
28124      * Customizable piece of the default paging text (defaults to "of %0")
28125      * @type String
28126      */
28127     afterPageText : "of {0}",
28128     /**
28129      * Customizable piece of the default paging text (defaults to "First Page")
28130      * @type String
28131      */
28132     firstText : "First Page",
28133     /**
28134      * Customizable piece of the default paging text (defaults to "Previous Page")
28135      * @type String
28136      */
28137     prevText : "Previous Page",
28138     /**
28139      * Customizable piece of the default paging text (defaults to "Next Page")
28140      * @type String
28141      */
28142     nextText : "Next Page",
28143     /**
28144      * Customizable piece of the default paging text (defaults to "Last Page")
28145      * @type String
28146      */
28147     lastText : "Last Page",
28148     /**
28149      * Customizable piece of the default paging text (defaults to "Refresh")
28150      * @type String
28151      */
28152     refreshText : "Refresh",
28153
28154     buttons : false,
28155     // private
28156     onRender : function(ct, position) 
28157     {
28158         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28159         this.navgroup.parentId = this.id;
28160         this.navgroup.onRender(this.el, null);
28161         // add the buttons to the navgroup
28162         
28163         if(this.displayInfo){
28164             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28165             this.displayEl = this.el.select('.x-paging-info', true).first();
28166 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28167 //            this.displayEl = navel.el.select('span',true).first();
28168         }
28169         
28170         var _this = this;
28171         
28172         if(this.buttons){
28173             Roo.each(_this.buttons, function(e){ // this might need to use render????
28174                Roo.factory(e).render(_this.el);
28175             });
28176         }
28177             
28178         Roo.each(_this.toolbarItems, function(e) {
28179             _this.navgroup.addItem(e);
28180         });
28181         
28182         
28183         this.first = this.navgroup.addItem({
28184             tooltip: this.firstText,
28185             cls: "prev btn-outline-secondary",
28186             html : ' <i class="fa fa-step-backward"></i>',
28187             disabled: true,
28188             preventDefault: true,
28189             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28190         });
28191         
28192         this.prev =  this.navgroup.addItem({
28193             tooltip: this.prevText,
28194             cls: "prev btn-outline-secondary",
28195             html : ' <i class="fa fa-backward"></i>',
28196             disabled: true,
28197             preventDefault: true,
28198             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28199         });
28200     //this.addSeparator();
28201         
28202         
28203         var field = this.navgroup.addItem( {
28204             tagtype : 'span',
28205             cls : 'x-paging-position  btn-outline-secondary',
28206              disabled: true,
28207             html : this.beforePageText  +
28208                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28209                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28210          } ); //?? escaped?
28211         
28212         this.field = field.el.select('input', true).first();
28213         this.field.on("keydown", this.onPagingKeydown, this);
28214         this.field.on("focus", function(){this.dom.select();});
28215     
28216     
28217         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28218         //this.field.setHeight(18);
28219         //this.addSeparator();
28220         this.next = this.navgroup.addItem({
28221             tooltip: this.nextText,
28222             cls: "next btn-outline-secondary",
28223             html : ' <i class="fa fa-forward"></i>',
28224             disabled: true,
28225             preventDefault: true,
28226             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28227         });
28228         this.last = this.navgroup.addItem({
28229             tooltip: this.lastText,
28230             html : ' <i class="fa fa-step-forward"></i>',
28231             cls: "next btn-outline-secondary",
28232             disabled: true,
28233             preventDefault: true,
28234             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28235         });
28236     //this.addSeparator();
28237         this.loading = this.navgroup.addItem({
28238             tooltip: this.refreshText,
28239             cls: "btn-outline-secondary",
28240             html : ' <i class="fa fa-refresh"></i>',
28241             preventDefault: true,
28242             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28243         });
28244         
28245     },
28246
28247     // private
28248     updateInfo : function(){
28249         if(this.displayEl){
28250             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28251             var msg = count == 0 ?
28252                 this.emptyMsg :
28253                 String.format(
28254                     this.displayMsg,
28255                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28256                 );
28257             this.displayEl.update(msg);
28258         }
28259     },
28260
28261     // private
28262     onLoad : function(ds, r, o)
28263     {
28264         this.cursor = o.params && o.params.start ? o.params.start : 0;
28265         
28266         var d = this.getPageData(),
28267             ap = d.activePage,
28268             ps = d.pages;
28269         
28270         
28271         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28272         this.field.dom.value = ap;
28273         this.first.setDisabled(ap == 1);
28274         this.prev.setDisabled(ap == 1);
28275         this.next.setDisabled(ap == ps);
28276         this.last.setDisabled(ap == ps);
28277         this.loading.enable();
28278         this.updateInfo();
28279     },
28280
28281     // private
28282     getPageData : function(){
28283         var total = this.ds.getTotalCount();
28284         return {
28285             total : total,
28286             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28287             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28288         };
28289     },
28290
28291     // private
28292     onLoadError : function(){
28293         this.loading.enable();
28294     },
28295
28296     // private
28297     onPagingKeydown : function(e){
28298         var k = e.getKey();
28299         var d = this.getPageData();
28300         if(k == e.RETURN){
28301             var v = this.field.dom.value, pageNum;
28302             if(!v || isNaN(pageNum = parseInt(v, 10))){
28303                 this.field.dom.value = d.activePage;
28304                 return;
28305             }
28306             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28307             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28308             e.stopEvent();
28309         }
28310         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))
28311         {
28312           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28313           this.field.dom.value = pageNum;
28314           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28315           e.stopEvent();
28316         }
28317         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28318         {
28319           var v = this.field.dom.value, pageNum; 
28320           var increment = (e.shiftKey) ? 10 : 1;
28321           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28322                 increment *= -1;
28323           }
28324           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28325             this.field.dom.value = d.activePage;
28326             return;
28327           }
28328           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28329           {
28330             this.field.dom.value = parseInt(v, 10) + increment;
28331             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28332             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28333           }
28334           e.stopEvent();
28335         }
28336     },
28337
28338     // private
28339     beforeLoad : function(){
28340         if(this.loading){
28341             this.loading.disable();
28342         }
28343     },
28344
28345     // private
28346     onClick : function(which){
28347         
28348         var ds = this.ds;
28349         if (!ds) {
28350             return;
28351         }
28352         
28353         switch(which){
28354             case "first":
28355                 ds.load({params:{start: 0, limit: this.pageSize}});
28356             break;
28357             case "prev":
28358                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28359             break;
28360             case "next":
28361                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28362             break;
28363             case "last":
28364                 var total = ds.getTotalCount();
28365                 var extra = total % this.pageSize;
28366                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28367                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28368             break;
28369             case "refresh":
28370                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28371             break;
28372         }
28373     },
28374
28375     /**
28376      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28377      * @param {Roo.data.Store} store The data store to unbind
28378      */
28379     unbind : function(ds){
28380         ds.un("beforeload", this.beforeLoad, this);
28381         ds.un("load", this.onLoad, this);
28382         ds.un("loadexception", this.onLoadError, this);
28383         ds.un("remove", this.updateInfo, this);
28384         ds.un("add", this.updateInfo, this);
28385         this.ds = undefined;
28386     },
28387
28388     /**
28389      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28390      * @param {Roo.data.Store} store The data store to bind
28391      */
28392     bind : function(ds){
28393         ds.on("beforeload", this.beforeLoad, this);
28394         ds.on("load", this.onLoad, this);
28395         ds.on("loadexception", this.onLoadError, this);
28396         ds.on("remove", this.updateInfo, this);
28397         ds.on("add", this.updateInfo, this);
28398         this.ds = ds;
28399     }
28400 });/*
28401  * - LGPL
28402  *
28403  * element
28404  * 
28405  */
28406
28407 /**
28408  * @class Roo.bootstrap.MessageBar
28409  * @extends Roo.bootstrap.Component
28410  * Bootstrap MessageBar class
28411  * @cfg {String} html contents of the MessageBar
28412  * @cfg {String} weight (info | success | warning | danger) default info
28413  * @cfg {String} beforeClass insert the bar before the given class
28414  * @cfg {Boolean} closable (true | false) default false
28415  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28416  * 
28417  * @constructor
28418  * Create a new Element
28419  * @param {Object} config The config object
28420  */
28421
28422 Roo.bootstrap.MessageBar = function(config){
28423     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28424 };
28425
28426 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28427     
28428     html: '',
28429     weight: 'info',
28430     closable: false,
28431     fixed: false,
28432     beforeClass: 'bootstrap-sticky-wrap',
28433     
28434     getAutoCreate : function(){
28435         
28436         var cfg = {
28437             tag: 'div',
28438             cls: 'alert alert-dismissable alert-' + this.weight,
28439             cn: [
28440                 {
28441                     tag: 'span',
28442                     cls: 'message',
28443                     html: this.html || ''
28444                 }
28445             ]
28446         };
28447         
28448         if(this.fixed){
28449             cfg.cls += ' alert-messages-fixed';
28450         }
28451         
28452         if(this.closable){
28453             cfg.cn.push({
28454                 tag: 'button',
28455                 cls: 'close',
28456                 html: 'x'
28457             });
28458         }
28459         
28460         return cfg;
28461     },
28462     
28463     onRender : function(ct, position)
28464     {
28465         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28466         
28467         if(!this.el){
28468             var cfg = Roo.apply({},  this.getAutoCreate());
28469             cfg.id = Roo.id();
28470             
28471             if (this.cls) {
28472                 cfg.cls += ' ' + this.cls;
28473             }
28474             if (this.style) {
28475                 cfg.style = this.style;
28476             }
28477             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28478             
28479             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28480         }
28481         
28482         this.el.select('>button.close').on('click', this.hide, this);
28483         
28484     },
28485     
28486     show : function()
28487     {
28488         if (!this.rendered) {
28489             this.render();
28490         }
28491         
28492         this.el.show();
28493         
28494         this.fireEvent('show', this);
28495         
28496     },
28497     
28498     hide : function()
28499     {
28500         if (!this.rendered) {
28501             this.render();
28502         }
28503         
28504         this.el.hide();
28505         
28506         this.fireEvent('hide', this);
28507     },
28508     
28509     update : function()
28510     {
28511 //        var e = this.el.dom.firstChild;
28512 //        
28513 //        if(this.closable){
28514 //            e = e.nextSibling;
28515 //        }
28516 //        
28517 //        e.data = this.html || '';
28518
28519         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28520     }
28521    
28522 });
28523
28524  
28525
28526      /*
28527  * - LGPL
28528  *
28529  * Graph
28530  * 
28531  */
28532
28533
28534 /**
28535  * @class Roo.bootstrap.Graph
28536  * @extends Roo.bootstrap.Component
28537  * Bootstrap Graph class
28538 > Prameters
28539  -sm {number} sm 4
28540  -md {number} md 5
28541  @cfg {String} graphtype  bar | vbar | pie
28542  @cfg {number} g_x coodinator | centre x (pie)
28543  @cfg {number} g_y coodinator | centre y (pie)
28544  @cfg {number} g_r radius (pie)
28545  @cfg {number} g_height height of the chart (respected by all elements in the set)
28546  @cfg {number} g_width width of the chart (respected by all elements in the set)
28547  @cfg {Object} title The title of the chart
28548     
28549  -{Array}  values
28550  -opts (object) options for the chart 
28551      o {
28552      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28553      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28554      o vgutter (number)
28555      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.
28556      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28557      o to
28558      o stretch (boolean)
28559      o }
28560  -opts (object) options for the pie
28561      o{
28562      o cut
28563      o startAngle (number)
28564      o endAngle (number)
28565      } 
28566  *
28567  * @constructor
28568  * Create a new Input
28569  * @param {Object} config The config object
28570  */
28571
28572 Roo.bootstrap.Graph = function(config){
28573     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28574     
28575     this.addEvents({
28576         // img events
28577         /**
28578          * @event click
28579          * The img click event for the img.
28580          * @param {Roo.EventObject} e
28581          */
28582         "click" : true
28583     });
28584 };
28585
28586 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28587     
28588     sm: 4,
28589     md: 5,
28590     graphtype: 'bar',
28591     g_height: 250,
28592     g_width: 400,
28593     g_x: 50,
28594     g_y: 50,
28595     g_r: 30,
28596     opts:{
28597         //g_colors: this.colors,
28598         g_type: 'soft',
28599         g_gutter: '20%'
28600
28601     },
28602     title : false,
28603
28604     getAutoCreate : function(){
28605         
28606         var cfg = {
28607             tag: 'div',
28608             html : null
28609         };
28610         
28611         
28612         return  cfg;
28613     },
28614
28615     onRender : function(ct,position){
28616         
28617         
28618         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28619         
28620         if (typeof(Raphael) == 'undefined') {
28621             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28622             return;
28623         }
28624         
28625         this.raphael = Raphael(this.el.dom);
28626         
28627                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28628                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28629                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28630                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28631                 /*
28632                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28633                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28634                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28635                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28636                 
28637                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28638                 r.barchart(330, 10, 300, 220, data1);
28639                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28640                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28641                 */
28642                 
28643                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28644                 // r.barchart(30, 30, 560, 250,  xdata, {
28645                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28646                 //     axis : "0 0 1 1",
28647                 //     axisxlabels :  xdata
28648                 //     //yvalues : cols,
28649                    
28650                 // });
28651 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28652 //        
28653 //        this.load(null,xdata,{
28654 //                axis : "0 0 1 1",
28655 //                axisxlabels :  xdata
28656 //                });
28657
28658     },
28659
28660     load : function(graphtype,xdata,opts)
28661     {
28662         this.raphael.clear();
28663         if(!graphtype) {
28664             graphtype = this.graphtype;
28665         }
28666         if(!opts){
28667             opts = this.opts;
28668         }
28669         var r = this.raphael,
28670             fin = function () {
28671                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28672             },
28673             fout = function () {
28674                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28675             },
28676             pfin = function() {
28677                 this.sector.stop();
28678                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28679
28680                 if (this.label) {
28681                     this.label[0].stop();
28682                     this.label[0].attr({ r: 7.5 });
28683                     this.label[1].attr({ "font-weight": 800 });
28684                 }
28685             },
28686             pfout = function() {
28687                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28688
28689                 if (this.label) {
28690                     this.label[0].animate({ r: 5 }, 500, "bounce");
28691                     this.label[1].attr({ "font-weight": 400 });
28692                 }
28693             };
28694
28695         switch(graphtype){
28696             case 'bar':
28697                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28698                 break;
28699             case 'hbar':
28700                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28701                 break;
28702             case 'pie':
28703 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28704 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28705 //            
28706                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28707                 
28708                 break;
28709
28710         }
28711         
28712         if(this.title){
28713             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28714         }
28715         
28716     },
28717     
28718     setTitle: function(o)
28719     {
28720         this.title = o;
28721     },
28722     
28723     initEvents: function() {
28724         
28725         if(!this.href){
28726             this.el.on('click', this.onClick, this);
28727         }
28728     },
28729     
28730     onClick : function(e)
28731     {
28732         Roo.log('img onclick');
28733         this.fireEvent('click', this, e);
28734     }
28735    
28736 });
28737
28738  
28739 /*
28740  * - LGPL
28741  *
28742  * numberBox
28743  * 
28744  */
28745 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28746
28747 /**
28748  * @class Roo.bootstrap.dash.NumberBox
28749  * @extends Roo.bootstrap.Component
28750  * Bootstrap NumberBox class
28751  * @cfg {String} headline Box headline
28752  * @cfg {String} content Box content
28753  * @cfg {String} icon Box icon
28754  * @cfg {String} footer Footer text
28755  * @cfg {String} fhref Footer href
28756  * 
28757  * @constructor
28758  * Create a new NumberBox
28759  * @param {Object} config The config object
28760  */
28761
28762
28763 Roo.bootstrap.dash.NumberBox = function(config){
28764     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28765     
28766 };
28767
28768 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28769     
28770     headline : '',
28771     content : '',
28772     icon : '',
28773     footer : '',
28774     fhref : '',
28775     ficon : '',
28776     
28777     getAutoCreate : function(){
28778         
28779         var cfg = {
28780             tag : 'div',
28781             cls : 'small-box ',
28782             cn : [
28783                 {
28784                     tag : 'div',
28785                     cls : 'inner',
28786                     cn :[
28787                         {
28788                             tag : 'h3',
28789                             cls : 'roo-headline',
28790                             html : this.headline
28791                         },
28792                         {
28793                             tag : 'p',
28794                             cls : 'roo-content',
28795                             html : this.content
28796                         }
28797                     ]
28798                 }
28799             ]
28800         };
28801         
28802         if(this.icon){
28803             cfg.cn.push({
28804                 tag : 'div',
28805                 cls : 'icon',
28806                 cn :[
28807                     {
28808                         tag : 'i',
28809                         cls : 'ion ' + this.icon
28810                     }
28811                 ]
28812             });
28813         }
28814         
28815         if(this.footer){
28816             var footer = {
28817                 tag : 'a',
28818                 cls : 'small-box-footer',
28819                 href : this.fhref || '#',
28820                 html : this.footer
28821             };
28822             
28823             cfg.cn.push(footer);
28824             
28825         }
28826         
28827         return  cfg;
28828     },
28829
28830     onRender : function(ct,position){
28831         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28832
28833
28834        
28835                 
28836     },
28837
28838     setHeadline: function (value)
28839     {
28840         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28841     },
28842     
28843     setFooter: function (value, href)
28844     {
28845         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28846         
28847         if(href){
28848             this.el.select('a.small-box-footer',true).first().attr('href', href);
28849         }
28850         
28851     },
28852
28853     setContent: function (value)
28854     {
28855         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28856     },
28857
28858     initEvents: function() 
28859     {   
28860         
28861     }
28862     
28863 });
28864
28865  
28866 /*
28867  * - LGPL
28868  *
28869  * TabBox
28870  * 
28871  */
28872 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28873
28874 /**
28875  * @class Roo.bootstrap.dash.TabBox
28876  * @extends Roo.bootstrap.Component
28877  * Bootstrap TabBox class
28878  * @cfg {String} title Title of the TabBox
28879  * @cfg {String} icon Icon of the TabBox
28880  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28881  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28882  * 
28883  * @constructor
28884  * Create a new TabBox
28885  * @param {Object} config The config object
28886  */
28887
28888
28889 Roo.bootstrap.dash.TabBox = function(config){
28890     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28891     this.addEvents({
28892         // raw events
28893         /**
28894          * @event addpane
28895          * When a pane is added
28896          * @param {Roo.bootstrap.dash.TabPane} pane
28897          */
28898         "addpane" : true,
28899         /**
28900          * @event activatepane
28901          * When a pane is activated
28902          * @param {Roo.bootstrap.dash.TabPane} pane
28903          */
28904         "activatepane" : true
28905         
28906          
28907     });
28908     
28909     this.panes = [];
28910 };
28911
28912 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28913
28914     title : '',
28915     icon : false,
28916     showtabs : true,
28917     tabScrollable : false,
28918     
28919     getChildContainer : function()
28920     {
28921         return this.el.select('.tab-content', true).first();
28922     },
28923     
28924     getAutoCreate : function(){
28925         
28926         var header = {
28927             tag: 'li',
28928             cls: 'pull-left header',
28929             html: this.title,
28930             cn : []
28931         };
28932         
28933         if(this.icon){
28934             header.cn.push({
28935                 tag: 'i',
28936                 cls: 'fa ' + this.icon
28937             });
28938         }
28939         
28940         var h = {
28941             tag: 'ul',
28942             cls: 'nav nav-tabs pull-right',
28943             cn: [
28944                 header
28945             ]
28946         };
28947         
28948         if(this.tabScrollable){
28949             h = {
28950                 tag: 'div',
28951                 cls: 'tab-header',
28952                 cn: [
28953                     {
28954                         tag: 'ul',
28955                         cls: 'nav nav-tabs pull-right',
28956                         cn: [
28957                             header
28958                         ]
28959                     }
28960                 ]
28961             };
28962         }
28963         
28964         var cfg = {
28965             tag: 'div',
28966             cls: 'nav-tabs-custom',
28967             cn: [
28968                 h,
28969                 {
28970                     tag: 'div',
28971                     cls: 'tab-content no-padding',
28972                     cn: []
28973                 }
28974             ]
28975         };
28976
28977         return  cfg;
28978     },
28979     initEvents : function()
28980     {
28981         //Roo.log('add add pane handler');
28982         this.on('addpane', this.onAddPane, this);
28983     },
28984      /**
28985      * Updates the box title
28986      * @param {String} html to set the title to.
28987      */
28988     setTitle : function(value)
28989     {
28990         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28991     },
28992     onAddPane : function(pane)
28993     {
28994         this.panes.push(pane);
28995         //Roo.log('addpane');
28996         //Roo.log(pane);
28997         // tabs are rendere left to right..
28998         if(!this.showtabs){
28999             return;
29000         }
29001         
29002         var ctr = this.el.select('.nav-tabs', true).first();
29003          
29004          
29005         var existing = ctr.select('.nav-tab',true);
29006         var qty = existing.getCount();;
29007         
29008         
29009         var tab = ctr.createChild({
29010             tag : 'li',
29011             cls : 'nav-tab' + (qty ? '' : ' active'),
29012             cn : [
29013                 {
29014                     tag : 'a',
29015                     href:'#',
29016                     html : pane.title
29017                 }
29018             ]
29019         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29020         pane.tab = tab;
29021         
29022         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29023         if (!qty) {
29024             pane.el.addClass('active');
29025         }
29026         
29027                 
29028     },
29029     onTabClick : function(ev,un,ob,pane)
29030     {
29031         //Roo.log('tab - prev default');
29032         ev.preventDefault();
29033         
29034         
29035         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29036         pane.tab.addClass('active');
29037         //Roo.log(pane.title);
29038         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29039         // technically we should have a deactivate event.. but maybe add later.
29040         // and it should not de-activate the selected tab...
29041         this.fireEvent('activatepane', pane);
29042         pane.el.addClass('active');
29043         pane.fireEvent('activate');
29044         
29045         
29046     },
29047     
29048     getActivePane : function()
29049     {
29050         var r = false;
29051         Roo.each(this.panes, function(p) {
29052             if(p.el.hasClass('active')){
29053                 r = p;
29054                 return false;
29055             }
29056             
29057             return;
29058         });
29059         
29060         return r;
29061     }
29062     
29063     
29064 });
29065
29066  
29067 /*
29068  * - LGPL
29069  *
29070  * Tab pane
29071  * 
29072  */
29073 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29074 /**
29075  * @class Roo.bootstrap.TabPane
29076  * @extends Roo.bootstrap.Component
29077  * Bootstrap TabPane class
29078  * @cfg {Boolean} active (false | true) Default false
29079  * @cfg {String} title title of panel
29080
29081  * 
29082  * @constructor
29083  * Create a new TabPane
29084  * @param {Object} config The config object
29085  */
29086
29087 Roo.bootstrap.dash.TabPane = function(config){
29088     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29089     
29090     this.addEvents({
29091         // raw events
29092         /**
29093          * @event activate
29094          * When a pane is activated
29095          * @param {Roo.bootstrap.dash.TabPane} pane
29096          */
29097         "activate" : true
29098          
29099     });
29100 };
29101
29102 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29103     
29104     active : false,
29105     title : '',
29106     
29107     // the tabBox that this is attached to.
29108     tab : false,
29109      
29110     getAutoCreate : function() 
29111     {
29112         var cfg = {
29113             tag: 'div',
29114             cls: 'tab-pane'
29115         };
29116         
29117         if(this.active){
29118             cfg.cls += ' active';
29119         }
29120         
29121         return cfg;
29122     },
29123     initEvents  : function()
29124     {
29125         //Roo.log('trigger add pane handler');
29126         this.parent().fireEvent('addpane', this)
29127     },
29128     
29129      /**
29130      * Updates the tab title 
29131      * @param {String} html to set the title to.
29132      */
29133     setTitle: function(str)
29134     {
29135         if (!this.tab) {
29136             return;
29137         }
29138         this.title = str;
29139         this.tab.select('a', true).first().dom.innerHTML = str;
29140         
29141     }
29142     
29143     
29144     
29145 });
29146
29147  
29148
29149
29150  /*
29151  * - LGPL
29152  *
29153  * menu
29154  * 
29155  */
29156 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29157
29158 /**
29159  * @class Roo.bootstrap.menu.Menu
29160  * @extends Roo.bootstrap.Component
29161  * Bootstrap Menu class - container for Menu
29162  * @cfg {String} html Text of the menu
29163  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29164  * @cfg {String} icon Font awesome icon
29165  * @cfg {String} pos Menu align to (top | bottom) default bottom
29166  * 
29167  * 
29168  * @constructor
29169  * Create a new Menu
29170  * @param {Object} config The config object
29171  */
29172
29173
29174 Roo.bootstrap.menu.Menu = function(config){
29175     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29176     
29177     this.addEvents({
29178         /**
29179          * @event beforeshow
29180          * Fires before this menu is displayed
29181          * @param {Roo.bootstrap.menu.Menu} this
29182          */
29183         beforeshow : true,
29184         /**
29185          * @event beforehide
29186          * Fires before this menu is hidden
29187          * @param {Roo.bootstrap.menu.Menu} this
29188          */
29189         beforehide : true,
29190         /**
29191          * @event show
29192          * Fires after this menu is displayed
29193          * @param {Roo.bootstrap.menu.Menu} this
29194          */
29195         show : true,
29196         /**
29197          * @event hide
29198          * Fires after this menu is hidden
29199          * @param {Roo.bootstrap.menu.Menu} this
29200          */
29201         hide : true,
29202         /**
29203          * @event click
29204          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29205          * @param {Roo.bootstrap.menu.Menu} this
29206          * @param {Roo.EventObject} e
29207          */
29208         click : true
29209     });
29210     
29211 };
29212
29213 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29214     
29215     submenu : false,
29216     html : '',
29217     weight : 'default',
29218     icon : false,
29219     pos : 'bottom',
29220     
29221     
29222     getChildContainer : function() {
29223         if(this.isSubMenu){
29224             return this.el;
29225         }
29226         
29227         return this.el.select('ul.dropdown-menu', true).first();  
29228     },
29229     
29230     getAutoCreate : function()
29231     {
29232         var text = [
29233             {
29234                 tag : 'span',
29235                 cls : 'roo-menu-text',
29236                 html : this.html
29237             }
29238         ];
29239         
29240         if(this.icon){
29241             text.unshift({
29242                 tag : 'i',
29243                 cls : 'fa ' + this.icon
29244             })
29245         }
29246         
29247         
29248         var cfg = {
29249             tag : 'div',
29250             cls : 'btn-group',
29251             cn : [
29252                 {
29253                     tag : 'button',
29254                     cls : 'dropdown-button btn btn-' + this.weight,
29255                     cn : text
29256                 },
29257                 {
29258                     tag : 'button',
29259                     cls : 'dropdown-toggle btn btn-' + this.weight,
29260                     cn : [
29261                         {
29262                             tag : 'span',
29263                             cls : 'caret'
29264                         }
29265                     ]
29266                 },
29267                 {
29268                     tag : 'ul',
29269                     cls : 'dropdown-menu'
29270                 }
29271             ]
29272             
29273         };
29274         
29275         if(this.pos == 'top'){
29276             cfg.cls += ' dropup';
29277         }
29278         
29279         if(this.isSubMenu){
29280             cfg = {
29281                 tag : 'ul',
29282                 cls : 'dropdown-menu'
29283             }
29284         }
29285         
29286         return cfg;
29287     },
29288     
29289     onRender : function(ct, position)
29290     {
29291         this.isSubMenu = ct.hasClass('dropdown-submenu');
29292         
29293         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29294     },
29295     
29296     initEvents : function() 
29297     {
29298         if(this.isSubMenu){
29299             return;
29300         }
29301         
29302         this.hidden = true;
29303         
29304         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29305         this.triggerEl.on('click', this.onTriggerPress, this);
29306         
29307         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29308         this.buttonEl.on('click', this.onClick, this);
29309         
29310     },
29311     
29312     list : function()
29313     {
29314         if(this.isSubMenu){
29315             return this.el;
29316         }
29317         
29318         return this.el.select('ul.dropdown-menu', true).first();
29319     },
29320     
29321     onClick : function(e)
29322     {
29323         this.fireEvent("click", this, e);
29324     },
29325     
29326     onTriggerPress  : function(e)
29327     {   
29328         if (this.isVisible()) {
29329             this.hide();
29330         } else {
29331             this.show();
29332         }
29333     },
29334     
29335     isVisible : function(){
29336         return !this.hidden;
29337     },
29338     
29339     show : function()
29340     {
29341         this.fireEvent("beforeshow", this);
29342         
29343         this.hidden = false;
29344         this.el.addClass('open');
29345         
29346         Roo.get(document).on("mouseup", this.onMouseUp, this);
29347         
29348         this.fireEvent("show", this);
29349         
29350         
29351     },
29352     
29353     hide : function()
29354     {
29355         this.fireEvent("beforehide", this);
29356         
29357         this.hidden = true;
29358         this.el.removeClass('open');
29359         
29360         Roo.get(document).un("mouseup", this.onMouseUp);
29361         
29362         this.fireEvent("hide", this);
29363     },
29364     
29365     onMouseUp : function()
29366     {
29367         this.hide();
29368     }
29369     
29370 });
29371
29372  
29373  /*
29374  * - LGPL
29375  *
29376  * menu item
29377  * 
29378  */
29379 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29380
29381 /**
29382  * @class Roo.bootstrap.menu.Item
29383  * @extends Roo.bootstrap.Component
29384  * Bootstrap MenuItem class
29385  * @cfg {Boolean} submenu (true | false) default false
29386  * @cfg {String} html text of the item
29387  * @cfg {String} href the link
29388  * @cfg {Boolean} disable (true | false) default false
29389  * @cfg {Boolean} preventDefault (true | false) default true
29390  * @cfg {String} icon Font awesome icon
29391  * @cfg {String} pos Submenu align to (left | right) default right 
29392  * 
29393  * 
29394  * @constructor
29395  * Create a new Item
29396  * @param {Object} config The config object
29397  */
29398
29399
29400 Roo.bootstrap.menu.Item = function(config){
29401     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29402     this.addEvents({
29403         /**
29404          * @event mouseover
29405          * Fires when the mouse is hovering over this menu
29406          * @param {Roo.bootstrap.menu.Item} this
29407          * @param {Roo.EventObject} e
29408          */
29409         mouseover : true,
29410         /**
29411          * @event mouseout
29412          * Fires when the mouse exits this menu
29413          * @param {Roo.bootstrap.menu.Item} this
29414          * @param {Roo.EventObject} e
29415          */
29416         mouseout : true,
29417         // raw events
29418         /**
29419          * @event click
29420          * The raw click event for the entire grid.
29421          * @param {Roo.EventObject} e
29422          */
29423         click : true
29424     });
29425 };
29426
29427 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29428     
29429     submenu : false,
29430     href : '',
29431     html : '',
29432     preventDefault: true,
29433     disable : false,
29434     icon : false,
29435     pos : 'right',
29436     
29437     getAutoCreate : function()
29438     {
29439         var text = [
29440             {
29441                 tag : 'span',
29442                 cls : 'roo-menu-item-text',
29443                 html : this.html
29444             }
29445         ];
29446         
29447         if(this.icon){
29448             text.unshift({
29449                 tag : 'i',
29450                 cls : 'fa ' + this.icon
29451             })
29452         }
29453         
29454         var cfg = {
29455             tag : 'li',
29456             cn : [
29457                 {
29458                     tag : 'a',
29459                     href : this.href || '#',
29460                     cn : text
29461                 }
29462             ]
29463         };
29464         
29465         if(this.disable){
29466             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29467         }
29468         
29469         if(this.submenu){
29470             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29471             
29472             if(this.pos == 'left'){
29473                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29474             }
29475         }
29476         
29477         return cfg;
29478     },
29479     
29480     initEvents : function() 
29481     {
29482         this.el.on('mouseover', this.onMouseOver, this);
29483         this.el.on('mouseout', this.onMouseOut, this);
29484         
29485         this.el.select('a', true).first().on('click', this.onClick, this);
29486         
29487     },
29488     
29489     onClick : function(e)
29490     {
29491         if(this.preventDefault){
29492             e.preventDefault();
29493         }
29494         
29495         this.fireEvent("click", this, e);
29496     },
29497     
29498     onMouseOver : function(e)
29499     {
29500         if(this.submenu && this.pos == 'left'){
29501             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29502         }
29503         
29504         this.fireEvent("mouseover", this, e);
29505     },
29506     
29507     onMouseOut : function(e)
29508     {
29509         this.fireEvent("mouseout", this, e);
29510     }
29511 });
29512
29513  
29514
29515  /*
29516  * - LGPL
29517  *
29518  * menu separator
29519  * 
29520  */
29521 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29522
29523 /**
29524  * @class Roo.bootstrap.menu.Separator
29525  * @extends Roo.bootstrap.Component
29526  * Bootstrap Separator class
29527  * 
29528  * @constructor
29529  * Create a new Separator
29530  * @param {Object} config The config object
29531  */
29532
29533
29534 Roo.bootstrap.menu.Separator = function(config){
29535     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29536 };
29537
29538 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29539     
29540     getAutoCreate : function(){
29541         var cfg = {
29542             tag : 'li',
29543             cls: 'dropdown-divider divider'
29544         };
29545         
29546         return cfg;
29547     }
29548    
29549 });
29550
29551  
29552
29553  /*
29554  * - LGPL
29555  *
29556  * Tooltip
29557  * 
29558  */
29559
29560 /**
29561  * @class Roo.bootstrap.Tooltip
29562  * Bootstrap Tooltip class
29563  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29564  * to determine which dom element triggers the tooltip.
29565  * 
29566  * It needs to add support for additional attributes like tooltip-position
29567  * 
29568  * @constructor
29569  * Create a new Toolti
29570  * @param {Object} config The config object
29571  */
29572
29573 Roo.bootstrap.Tooltip = function(config){
29574     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29575     
29576     this.alignment = Roo.bootstrap.Tooltip.alignment;
29577     
29578     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29579         this.alignment = config.alignment;
29580     }
29581     
29582 };
29583
29584 Roo.apply(Roo.bootstrap.Tooltip, {
29585     /**
29586      * @function init initialize tooltip monitoring.
29587      * @static
29588      */
29589     currentEl : false,
29590     currentTip : false,
29591     currentRegion : false,
29592     
29593     //  init : delay?
29594     
29595     init : function()
29596     {
29597         Roo.get(document).on('mouseover', this.enter ,this);
29598         Roo.get(document).on('mouseout', this.leave, this);
29599          
29600         
29601         this.currentTip = new Roo.bootstrap.Tooltip();
29602     },
29603     
29604     enter : function(ev)
29605     {
29606         var dom = ev.getTarget();
29607         
29608         //Roo.log(['enter',dom]);
29609         var el = Roo.fly(dom);
29610         if (this.currentEl) {
29611             //Roo.log(dom);
29612             //Roo.log(this.currentEl);
29613             //Roo.log(this.currentEl.contains(dom));
29614             if (this.currentEl == el) {
29615                 return;
29616             }
29617             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29618                 return;
29619             }
29620
29621         }
29622         
29623         if (this.currentTip.el) {
29624             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29625         }    
29626         //Roo.log(ev);
29627         
29628         if(!el || el.dom == document){
29629             return;
29630         }
29631         
29632         var bindEl = el; 
29633         var pel = false;
29634         if (!el.attr('tooltip')) {
29635             pel = el.findParent("[tooltip]");
29636             if (pel) {
29637                 bindEl = Roo.get(pel);
29638             }
29639         }
29640         
29641        
29642         
29643         // you can not look for children, as if el is the body.. then everythign is the child..
29644         if (!pel && !el.attr('tooltip')) { //
29645             if (!el.select("[tooltip]").elements.length) {
29646                 return;
29647             }
29648             // is the mouse over this child...?
29649             bindEl = el.select("[tooltip]").first();
29650             var xy = ev.getXY();
29651             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29652                 //Roo.log("not in region.");
29653                 return;
29654             }
29655             //Roo.log("child element over..");
29656             
29657         }
29658         this.currentEl = el;
29659         this.currentTip.bind(bindEl);
29660         this.currentRegion = Roo.lib.Region.getRegion(dom);
29661         this.currentTip.enter();
29662         
29663     },
29664     leave : function(ev)
29665     {
29666         var dom = ev.getTarget();
29667         //Roo.log(['leave',dom]);
29668         if (!this.currentEl) {
29669             return;
29670         }
29671         
29672         
29673         if (dom != this.currentEl.dom) {
29674             return;
29675         }
29676         var xy = ev.getXY();
29677         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29678             return;
29679         }
29680         // only activate leave if mouse cursor is outside... bounding box..
29681         
29682         
29683         
29684         
29685         if (this.currentTip) {
29686             this.currentTip.leave();
29687         }
29688         //Roo.log('clear currentEl');
29689         this.currentEl = false;
29690         
29691         
29692     },
29693     alignment : {
29694         'left' : ['r-l', [-2,0], 'right'],
29695         'right' : ['l-r', [2,0], 'left'],
29696         'bottom' : ['t-b', [0,2], 'top'],
29697         'top' : [ 'b-t', [0,-2], 'bottom']
29698     }
29699     
29700 });
29701
29702
29703 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29704     
29705     
29706     bindEl : false,
29707     
29708     delay : null, // can be { show : 300 , hide: 500}
29709     
29710     timeout : null,
29711     
29712     hoverState : null, //???
29713     
29714     placement : 'bottom', 
29715     
29716     alignment : false,
29717     
29718     getAutoCreate : function(){
29719     
29720         var cfg = {
29721            cls : 'tooltip',   
29722            role : 'tooltip',
29723            cn : [
29724                 {
29725                     cls : 'tooltip-arrow arrow'
29726                 },
29727                 {
29728                     cls : 'tooltip-inner'
29729                 }
29730            ]
29731         };
29732         
29733         return cfg;
29734     },
29735     bind : function(el)
29736     {
29737         this.bindEl = el;
29738     },
29739     
29740     initEvents : function()
29741     {
29742         this.arrowEl = this.el.select('.arrow', true).first();
29743         this.innerEl = this.el.select('.tooltip-inner', true).first();
29744     },
29745     
29746     enter : function () {
29747        
29748         if (this.timeout != null) {
29749             clearTimeout(this.timeout);
29750         }
29751         
29752         this.hoverState = 'in';
29753          //Roo.log("enter - show");
29754         if (!this.delay || !this.delay.show) {
29755             this.show();
29756             return;
29757         }
29758         var _t = this;
29759         this.timeout = setTimeout(function () {
29760             if (_t.hoverState == 'in') {
29761                 _t.show();
29762             }
29763         }, this.delay.show);
29764     },
29765     leave : function()
29766     {
29767         clearTimeout(this.timeout);
29768     
29769         this.hoverState = 'out';
29770          if (!this.delay || !this.delay.hide) {
29771             this.hide();
29772             return;
29773         }
29774        
29775         var _t = this;
29776         this.timeout = setTimeout(function () {
29777             //Roo.log("leave - timeout");
29778             
29779             if (_t.hoverState == 'out') {
29780                 _t.hide();
29781                 Roo.bootstrap.Tooltip.currentEl = false;
29782             }
29783         }, delay);
29784     },
29785     
29786     show : function (msg)
29787     {
29788         if (!this.el) {
29789             this.render(document.body);
29790         }
29791         // set content.
29792         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29793         
29794         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29795         
29796         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29797         
29798         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29799                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29800         
29801         var placement = typeof this.placement == 'function' ?
29802             this.placement.call(this, this.el, on_el) :
29803             this.placement;
29804             
29805         var autoToken = /\s?auto?\s?/i;
29806         var autoPlace = autoToken.test(placement);
29807         if (autoPlace) {
29808             placement = placement.replace(autoToken, '') || 'top';
29809         }
29810         
29811         //this.el.detach()
29812         //this.el.setXY([0,0]);
29813         this.el.show();
29814         //this.el.dom.style.display='block';
29815         
29816         //this.el.appendTo(on_el);
29817         
29818         var p = this.getPosition();
29819         var box = this.el.getBox();
29820         
29821         if (autoPlace) {
29822             // fixme..
29823         }
29824         
29825         var align = this.alignment[placement];
29826         
29827         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29828         
29829         if(placement == 'top' || placement == 'bottom'){
29830             if(xy[0] < 0){
29831                 placement = 'right';
29832             }
29833             
29834             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29835                 placement = 'left';
29836             }
29837             
29838             var scroll = Roo.select('body', true).first().getScroll();
29839             
29840             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29841                 placement = 'top';
29842             }
29843             
29844             align = this.alignment[placement];
29845             
29846             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29847             
29848         }
29849         
29850         var elems = document.getElementsByTagName('div');
29851         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29852         for (var i = 0; i < elems.length; i++) {
29853           var zindex = Number.parseInt(
29854                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29855                 10
29856           );
29857           if (zindex > highest) {
29858             highest = zindex;
29859           }
29860         }
29861         
29862         
29863         
29864         this.el.dom.style.zIndex = highest;
29865         
29866         this.el.alignTo(this.bindEl, align[0],align[1]);
29867         //var arrow = this.el.select('.arrow',true).first();
29868         //arrow.set(align[2], 
29869         
29870         this.el.addClass(placement);
29871         this.el.addClass("bs-tooltip-"+ placement);
29872         
29873         this.el.addClass('in fade show');
29874         
29875         this.hoverState = null;
29876         
29877         if (this.el.hasClass('fade')) {
29878             // fade it?
29879         }
29880         
29881         
29882         
29883         
29884         
29885     },
29886     hide : function()
29887     {
29888          
29889         if (!this.el) {
29890             return;
29891         }
29892         //this.el.setXY([0,0]);
29893         this.el.removeClass(['show', 'in']);
29894         //this.el.hide();
29895         
29896     }
29897     
29898 });
29899  
29900
29901  /*
29902  * - LGPL
29903  *
29904  * Location Picker
29905  * 
29906  */
29907
29908 /**
29909  * @class Roo.bootstrap.LocationPicker
29910  * @extends Roo.bootstrap.Component
29911  * Bootstrap LocationPicker class
29912  * @cfg {Number} latitude Position when init default 0
29913  * @cfg {Number} longitude Position when init default 0
29914  * @cfg {Number} zoom default 15
29915  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29916  * @cfg {Boolean} mapTypeControl default false
29917  * @cfg {Boolean} disableDoubleClickZoom default false
29918  * @cfg {Boolean} scrollwheel default true
29919  * @cfg {Boolean} streetViewControl default false
29920  * @cfg {Number} radius default 0
29921  * @cfg {String} locationName
29922  * @cfg {Boolean} draggable default true
29923  * @cfg {Boolean} enableAutocomplete default false
29924  * @cfg {Boolean} enableReverseGeocode default true
29925  * @cfg {String} markerTitle
29926  * 
29927  * @constructor
29928  * Create a new LocationPicker
29929  * @param {Object} config The config object
29930  */
29931
29932
29933 Roo.bootstrap.LocationPicker = function(config){
29934     
29935     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29936     
29937     this.addEvents({
29938         /**
29939          * @event initial
29940          * Fires when the picker initialized.
29941          * @param {Roo.bootstrap.LocationPicker} this
29942          * @param {Google Location} location
29943          */
29944         initial : true,
29945         /**
29946          * @event positionchanged
29947          * Fires when the picker position changed.
29948          * @param {Roo.bootstrap.LocationPicker} this
29949          * @param {Google Location} location
29950          */
29951         positionchanged : true,
29952         /**
29953          * @event resize
29954          * Fires when the map resize.
29955          * @param {Roo.bootstrap.LocationPicker} this
29956          */
29957         resize : true,
29958         /**
29959          * @event show
29960          * Fires when the map show.
29961          * @param {Roo.bootstrap.LocationPicker} this
29962          */
29963         show : true,
29964         /**
29965          * @event hide
29966          * Fires when the map hide.
29967          * @param {Roo.bootstrap.LocationPicker} this
29968          */
29969         hide : true,
29970         /**
29971          * @event mapClick
29972          * Fires when click the map.
29973          * @param {Roo.bootstrap.LocationPicker} this
29974          * @param {Map event} e
29975          */
29976         mapClick : true,
29977         /**
29978          * @event mapRightClick
29979          * Fires when right click the map.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          * @param {Map event} e
29982          */
29983         mapRightClick : true,
29984         /**
29985          * @event markerClick
29986          * Fires when click the marker.
29987          * @param {Roo.bootstrap.LocationPicker} this
29988          * @param {Map event} e
29989          */
29990         markerClick : true,
29991         /**
29992          * @event markerRightClick
29993          * Fires when right click the marker.
29994          * @param {Roo.bootstrap.LocationPicker} this
29995          * @param {Map event} e
29996          */
29997         markerRightClick : true,
29998         /**
29999          * @event OverlayViewDraw
30000          * Fires when OverlayView Draw
30001          * @param {Roo.bootstrap.LocationPicker} this
30002          */
30003         OverlayViewDraw : true,
30004         /**
30005          * @event OverlayViewOnAdd
30006          * Fires when OverlayView Draw
30007          * @param {Roo.bootstrap.LocationPicker} this
30008          */
30009         OverlayViewOnAdd : true,
30010         /**
30011          * @event OverlayViewOnRemove
30012          * Fires when OverlayView Draw
30013          * @param {Roo.bootstrap.LocationPicker} this
30014          */
30015         OverlayViewOnRemove : true,
30016         /**
30017          * @event OverlayViewShow
30018          * Fires when OverlayView Draw
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Pixel} cpx
30021          */
30022         OverlayViewShow : true,
30023         /**
30024          * @event OverlayViewHide
30025          * Fires when OverlayView Draw
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          */
30028         OverlayViewHide : true,
30029         /**
30030          * @event loadexception
30031          * Fires when load google lib failed.
30032          * @param {Roo.bootstrap.LocationPicker} this
30033          */
30034         loadexception : true
30035     });
30036         
30037 };
30038
30039 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30040     
30041     gMapContext: false,
30042     
30043     latitude: 0,
30044     longitude: 0,
30045     zoom: 15,
30046     mapTypeId: false,
30047     mapTypeControl: false,
30048     disableDoubleClickZoom: false,
30049     scrollwheel: true,
30050     streetViewControl: false,
30051     radius: 0,
30052     locationName: '',
30053     draggable: true,
30054     enableAutocomplete: false,
30055     enableReverseGeocode: true,
30056     markerTitle: '',
30057     
30058     getAutoCreate: function()
30059     {
30060
30061         var cfg = {
30062             tag: 'div',
30063             cls: 'roo-location-picker'
30064         };
30065         
30066         return cfg
30067     },
30068     
30069     initEvents: function(ct, position)
30070     {       
30071         if(!this.el.getWidth() || this.isApplied()){
30072             return;
30073         }
30074         
30075         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30076         
30077         this.initial();
30078     },
30079     
30080     initial: function()
30081     {
30082         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30083             this.fireEvent('loadexception', this);
30084             return;
30085         }
30086         
30087         if(!this.mapTypeId){
30088             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30089         }
30090         
30091         this.gMapContext = this.GMapContext();
30092         
30093         this.initOverlayView();
30094         
30095         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30096         
30097         var _this = this;
30098                 
30099         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30100             _this.setPosition(_this.gMapContext.marker.position);
30101         });
30102         
30103         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30104             _this.fireEvent('mapClick', this, event);
30105             
30106         });
30107
30108         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30109             _this.fireEvent('mapRightClick', this, event);
30110             
30111         });
30112         
30113         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30114             _this.fireEvent('markerClick', this, event);
30115             
30116         });
30117
30118         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30119             _this.fireEvent('markerRightClick', this, event);
30120             
30121         });
30122         
30123         this.setPosition(this.gMapContext.location);
30124         
30125         this.fireEvent('initial', this, this.gMapContext.location);
30126     },
30127     
30128     initOverlayView: function()
30129     {
30130         var _this = this;
30131         
30132         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30133             
30134             draw: function()
30135             {
30136                 _this.fireEvent('OverlayViewDraw', _this);
30137             },
30138             
30139             onAdd: function()
30140             {
30141                 _this.fireEvent('OverlayViewOnAdd', _this);
30142             },
30143             
30144             onRemove: function()
30145             {
30146                 _this.fireEvent('OverlayViewOnRemove', _this);
30147             },
30148             
30149             show: function(cpx)
30150             {
30151                 _this.fireEvent('OverlayViewShow', _this, cpx);
30152             },
30153             
30154             hide: function()
30155             {
30156                 _this.fireEvent('OverlayViewHide', _this);
30157             }
30158             
30159         });
30160     },
30161     
30162     fromLatLngToContainerPixel: function(event)
30163     {
30164         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30165     },
30166     
30167     isApplied: function() 
30168     {
30169         return this.getGmapContext() == false ? false : true;
30170     },
30171     
30172     getGmapContext: function() 
30173     {
30174         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30175     },
30176     
30177     GMapContext: function() 
30178     {
30179         var position = new google.maps.LatLng(this.latitude, this.longitude);
30180         
30181         var _map = new google.maps.Map(this.el.dom, {
30182             center: position,
30183             zoom: this.zoom,
30184             mapTypeId: this.mapTypeId,
30185             mapTypeControl: this.mapTypeControl,
30186             disableDoubleClickZoom: this.disableDoubleClickZoom,
30187             scrollwheel: this.scrollwheel,
30188             streetViewControl: this.streetViewControl,
30189             locationName: this.locationName,
30190             draggable: this.draggable,
30191             enableAutocomplete: this.enableAutocomplete,
30192             enableReverseGeocode: this.enableReverseGeocode
30193         });
30194         
30195         var _marker = new google.maps.Marker({
30196             position: position,
30197             map: _map,
30198             title: this.markerTitle,
30199             draggable: this.draggable
30200         });
30201         
30202         return {
30203             map: _map,
30204             marker: _marker,
30205             circle: null,
30206             location: position,
30207             radius: this.radius,
30208             locationName: this.locationName,
30209             addressComponents: {
30210                 formatted_address: null,
30211                 addressLine1: null,
30212                 addressLine2: null,
30213                 streetName: null,
30214                 streetNumber: null,
30215                 city: null,
30216                 district: null,
30217                 state: null,
30218                 stateOrProvince: null
30219             },
30220             settings: this,
30221             domContainer: this.el.dom,
30222             geodecoder: new google.maps.Geocoder()
30223         };
30224     },
30225     
30226     drawCircle: function(center, radius, options) 
30227     {
30228         if (this.gMapContext.circle != null) {
30229             this.gMapContext.circle.setMap(null);
30230         }
30231         if (radius > 0) {
30232             radius *= 1;
30233             options = Roo.apply({}, options, {
30234                 strokeColor: "#0000FF",
30235                 strokeOpacity: .35,
30236                 strokeWeight: 2,
30237                 fillColor: "#0000FF",
30238                 fillOpacity: .2
30239             });
30240             
30241             options.map = this.gMapContext.map;
30242             options.radius = radius;
30243             options.center = center;
30244             this.gMapContext.circle = new google.maps.Circle(options);
30245             return this.gMapContext.circle;
30246         }
30247         
30248         return null;
30249     },
30250     
30251     setPosition: function(location) 
30252     {
30253         this.gMapContext.location = location;
30254         this.gMapContext.marker.setPosition(location);
30255         this.gMapContext.map.panTo(location);
30256         this.drawCircle(location, this.gMapContext.radius, {});
30257         
30258         var _this = this;
30259         
30260         if (this.gMapContext.settings.enableReverseGeocode) {
30261             this.gMapContext.geodecoder.geocode({
30262                 latLng: this.gMapContext.location
30263             }, function(results, status) {
30264                 
30265                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30266                     _this.gMapContext.locationName = results[0].formatted_address;
30267                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30268                     
30269                     _this.fireEvent('positionchanged', this, location);
30270                 }
30271             });
30272             
30273             return;
30274         }
30275         
30276         this.fireEvent('positionchanged', this, location);
30277     },
30278     
30279     resize: function()
30280     {
30281         google.maps.event.trigger(this.gMapContext.map, "resize");
30282         
30283         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30284         
30285         this.fireEvent('resize', this);
30286     },
30287     
30288     setPositionByLatLng: function(latitude, longitude)
30289     {
30290         this.setPosition(new google.maps.LatLng(latitude, longitude));
30291     },
30292     
30293     getCurrentPosition: function() 
30294     {
30295         return {
30296             latitude: this.gMapContext.location.lat(),
30297             longitude: this.gMapContext.location.lng()
30298         };
30299     },
30300     
30301     getAddressName: function() 
30302     {
30303         return this.gMapContext.locationName;
30304     },
30305     
30306     getAddressComponents: function() 
30307     {
30308         return this.gMapContext.addressComponents;
30309     },
30310     
30311     address_component_from_google_geocode: function(address_components) 
30312     {
30313         var result = {};
30314         
30315         for (var i = 0; i < address_components.length; i++) {
30316             var component = address_components[i];
30317             if (component.types.indexOf("postal_code") >= 0) {
30318                 result.postalCode = component.short_name;
30319             } else if (component.types.indexOf("street_number") >= 0) {
30320                 result.streetNumber = component.short_name;
30321             } else if (component.types.indexOf("route") >= 0) {
30322                 result.streetName = component.short_name;
30323             } else if (component.types.indexOf("neighborhood") >= 0) {
30324                 result.city = component.short_name;
30325             } else if (component.types.indexOf("locality") >= 0) {
30326                 result.city = component.short_name;
30327             } else if (component.types.indexOf("sublocality") >= 0) {
30328                 result.district = component.short_name;
30329             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30330                 result.stateOrProvince = component.short_name;
30331             } else if (component.types.indexOf("country") >= 0) {
30332                 result.country = component.short_name;
30333             }
30334         }
30335         
30336         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30337         result.addressLine2 = "";
30338         return result;
30339     },
30340     
30341     setZoomLevel: function(zoom)
30342     {
30343         this.gMapContext.map.setZoom(zoom);
30344     },
30345     
30346     show: function()
30347     {
30348         if(!this.el){
30349             return;
30350         }
30351         
30352         this.el.show();
30353         
30354         this.resize();
30355         
30356         this.fireEvent('show', this);
30357     },
30358     
30359     hide: function()
30360     {
30361         if(!this.el){
30362             return;
30363         }
30364         
30365         this.el.hide();
30366         
30367         this.fireEvent('hide', this);
30368     }
30369     
30370 });
30371
30372 Roo.apply(Roo.bootstrap.LocationPicker, {
30373     
30374     OverlayView : function(map, options)
30375     {
30376         options = options || {};
30377         
30378         this.setMap(map);
30379     }
30380     
30381     
30382 });/**
30383  * @class Roo.bootstrap.Alert
30384  * @extends Roo.bootstrap.Component
30385  * Bootstrap Alert class - shows an alert area box
30386  * eg
30387  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30388   Enter a valid email address
30389 </div>
30390  * @licence LGPL
30391  * @cfg {String} title The title of alert
30392  * @cfg {String} html The content of alert
30393  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30394  * @cfg {String} fa font-awesomeicon
30395  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30396  * @cfg {Boolean} close true to show a x closer
30397  * 
30398  * 
30399  * @constructor
30400  * Create a new alert
30401  * @param {Object} config The config object
30402  */
30403
30404
30405 Roo.bootstrap.Alert = function(config){
30406     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30407     
30408 };
30409
30410 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30411     
30412     title: '',
30413     html: '',
30414     weight: false,
30415     fa: false,
30416     faicon: false, // BC
30417     close : false,
30418     
30419     
30420     getAutoCreate : function()
30421     {
30422         
30423         var cfg = {
30424             tag : 'div',
30425             cls : 'alert',
30426             cn : [
30427                 {
30428                     tag: 'button',
30429                     type :  "button",
30430                     cls: "close",
30431                     html : '×',
30432                     style : this.close ? '' : 'display:none'
30433                 },
30434                 {
30435                     tag : 'i',
30436                     cls : 'roo-alert-icon'
30437                     
30438                 },
30439                 {
30440                     tag : 'b',
30441                     cls : 'roo-alert-title',
30442                     html : this.title
30443                 },
30444                 {
30445                     tag : 'span',
30446                     cls : 'roo-alert-text',
30447                     html : this.html
30448                 }
30449             ]
30450         };
30451         
30452         if(this.faicon){
30453             cfg.cn[0].cls += ' fa ' + this.faicon;
30454         }
30455         if(this.fa){
30456             cfg.cn[0].cls += ' fa ' + this.fa;
30457         }
30458         
30459         if(this.weight){
30460             cfg.cls += ' alert-' + this.weight;
30461         }
30462         
30463         return cfg;
30464     },
30465     
30466     initEvents: function() 
30467     {
30468         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30469         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30470         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30471         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30472         if (this.seconds > 0) {
30473             this.hide.defer(this.seconds, this);
30474         }
30475     },
30476     /**
30477      * Set the Title Message HTML
30478      * @param {String} html
30479      */
30480     setTitle : function(str)
30481     {
30482         this.titleEl.dom.innerHTML = str;
30483     },
30484      
30485      /**
30486      * Set the Body Message HTML
30487      * @param {String} html
30488      */
30489     setHtml : function(str)
30490     {
30491         this.htmlEl.dom.innerHTML = str;
30492     },
30493     /**
30494      * Set the Weight of the alert
30495      * @param {String} (success|info|warning|danger) weight
30496      */
30497     
30498     setWeight : function(weight)
30499     {
30500         if(this.weight){
30501             this.el.removeClass('alert-' + this.weight);
30502         }
30503         
30504         this.weight = weight;
30505         
30506         this.el.addClass('alert-' + this.weight);
30507     },
30508       /**
30509      * Set the Icon of the alert
30510      * @param {String} see fontawsome names (name without the 'fa-' bit)
30511      */
30512     setIcon : function(icon)
30513     {
30514         if(this.faicon){
30515             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30516         }
30517         
30518         this.faicon = icon;
30519         
30520         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30521     },
30522     /**
30523      * Hide the Alert
30524      */
30525     hide: function() 
30526     {
30527         this.el.hide();   
30528     },
30529     /**
30530      * Show the Alert
30531      */
30532     show: function() 
30533     {  
30534         this.el.show();   
30535     }
30536     
30537 });
30538
30539  
30540 /*
30541 * Licence: LGPL
30542 */
30543
30544 /**
30545  * @class Roo.bootstrap.UploadCropbox
30546  * @extends Roo.bootstrap.Component
30547  * Bootstrap UploadCropbox class
30548  * @cfg {String} emptyText show when image has been loaded
30549  * @cfg {String} rotateNotify show when image too small to rotate
30550  * @cfg {Number} errorTimeout default 3000
30551  * @cfg {Number} minWidth default 300
30552  * @cfg {Number} minHeight default 300
30553  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30554  * @cfg {Boolean} isDocument (true|false) default false
30555  * @cfg {String} url action url
30556  * @cfg {String} paramName default 'imageUpload'
30557  * @cfg {String} method default POST
30558  * @cfg {Boolean} loadMask (true|false) default true
30559  * @cfg {Boolean} loadingText default 'Loading...'
30560  * 
30561  * @constructor
30562  * Create a new UploadCropbox
30563  * @param {Object} config The config object
30564  */
30565
30566 Roo.bootstrap.UploadCropbox = function(config){
30567     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30568     
30569     this.addEvents({
30570         /**
30571          * @event beforeselectfile
30572          * Fire before select file
30573          * @param {Roo.bootstrap.UploadCropbox} this
30574          */
30575         "beforeselectfile" : true,
30576         /**
30577          * @event initial
30578          * Fire after initEvent
30579          * @param {Roo.bootstrap.UploadCropbox} this
30580          */
30581         "initial" : true,
30582         /**
30583          * @event crop
30584          * Fire after initEvent
30585          * @param {Roo.bootstrap.UploadCropbox} this
30586          * @param {String} data
30587          */
30588         "crop" : true,
30589         /**
30590          * @event prepare
30591          * Fire when preparing the file data
30592          * @param {Roo.bootstrap.UploadCropbox} this
30593          * @param {Object} file
30594          */
30595         "prepare" : true,
30596         /**
30597          * @event exception
30598          * Fire when get exception
30599          * @param {Roo.bootstrap.UploadCropbox} this
30600          * @param {XMLHttpRequest} xhr
30601          */
30602         "exception" : true,
30603         /**
30604          * @event beforeloadcanvas
30605          * Fire before load the canvas
30606          * @param {Roo.bootstrap.UploadCropbox} this
30607          * @param {String} src
30608          */
30609         "beforeloadcanvas" : true,
30610         /**
30611          * @event trash
30612          * Fire when trash image
30613          * @param {Roo.bootstrap.UploadCropbox} this
30614          */
30615         "trash" : true,
30616         /**
30617          * @event download
30618          * Fire when download the image
30619          * @param {Roo.bootstrap.UploadCropbox} this
30620          */
30621         "download" : true,
30622         /**
30623          * @event footerbuttonclick
30624          * Fire when footerbuttonclick
30625          * @param {Roo.bootstrap.UploadCropbox} this
30626          * @param {String} type
30627          */
30628         "footerbuttonclick" : true,
30629         /**
30630          * @event resize
30631          * Fire when resize
30632          * @param {Roo.bootstrap.UploadCropbox} this
30633          */
30634         "resize" : true,
30635         /**
30636          * @event rotate
30637          * Fire when rotate the image
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          * @param {String} pos
30640          */
30641         "rotate" : true,
30642         /**
30643          * @event inspect
30644          * Fire when inspect the file
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          * @param {Object} file
30647          */
30648         "inspect" : true,
30649         /**
30650          * @event upload
30651          * Fire when xhr upload the file
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          * @param {Object} data
30654          */
30655         "upload" : true,
30656         /**
30657          * @event arrange
30658          * Fire when arrange the file data
30659          * @param {Roo.bootstrap.UploadCropbox} this
30660          * @param {Object} formData
30661          */
30662         "arrange" : true
30663     });
30664     
30665     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30666 };
30667
30668 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30669     
30670     emptyText : 'Click to upload image',
30671     rotateNotify : 'Image is too small to rotate',
30672     errorTimeout : 3000,
30673     scale : 0,
30674     baseScale : 1,
30675     rotate : 0,
30676     dragable : false,
30677     pinching : false,
30678     mouseX : 0,
30679     mouseY : 0,
30680     cropData : false,
30681     minWidth : 300,
30682     minHeight : 300,
30683     file : false,
30684     exif : {},
30685     baseRotate : 1,
30686     cropType : 'image/jpeg',
30687     buttons : false,
30688     canvasLoaded : false,
30689     isDocument : false,
30690     method : 'POST',
30691     paramName : 'imageUpload',
30692     loadMask : true,
30693     loadingText : 'Loading...',
30694     maskEl : false,
30695     
30696     getAutoCreate : function()
30697     {
30698         var cfg = {
30699             tag : 'div',
30700             cls : 'roo-upload-cropbox',
30701             cn : [
30702                 {
30703                     tag : 'input',
30704                     cls : 'roo-upload-cropbox-selector',
30705                     type : 'file'
30706                 },
30707                 {
30708                     tag : 'div',
30709                     cls : 'roo-upload-cropbox-body',
30710                     style : 'cursor:pointer',
30711                     cn : [
30712                         {
30713                             tag : 'div',
30714                             cls : 'roo-upload-cropbox-preview'
30715                         },
30716                         {
30717                             tag : 'div',
30718                             cls : 'roo-upload-cropbox-thumb'
30719                         },
30720                         {
30721                             tag : 'div',
30722                             cls : 'roo-upload-cropbox-empty-notify',
30723                             html : this.emptyText
30724                         },
30725                         {
30726                             tag : 'div',
30727                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30728                             html : this.rotateNotify
30729                         }
30730                     ]
30731                 },
30732                 {
30733                     tag : 'div',
30734                     cls : 'roo-upload-cropbox-footer',
30735                     cn : {
30736                         tag : 'div',
30737                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30738                         cn : []
30739                     }
30740                 }
30741             ]
30742         };
30743         
30744         return cfg;
30745     },
30746     
30747     onRender : function(ct, position)
30748     {
30749         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30750         
30751         if (this.buttons.length) {
30752             
30753             Roo.each(this.buttons, function(bb) {
30754                 
30755                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30756                 
30757                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30758                 
30759             }, this);
30760         }
30761         
30762         if(this.loadMask){
30763             this.maskEl = this.el;
30764         }
30765     },
30766     
30767     initEvents : function()
30768     {
30769         this.urlAPI = (window.createObjectURL && window) || 
30770                                 (window.URL && URL.revokeObjectURL && URL) || 
30771                                 (window.webkitURL && webkitURL);
30772                         
30773         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30774         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30775         
30776         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30777         this.selectorEl.hide();
30778         
30779         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30780         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30781         
30782         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30783         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30784         this.thumbEl.hide();
30785         
30786         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30787         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30788         
30789         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30790         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30791         this.errorEl.hide();
30792         
30793         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30794         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795         this.footerEl.hide();
30796         
30797         this.setThumbBoxSize();
30798         
30799         this.bind();
30800         
30801         this.resize();
30802         
30803         this.fireEvent('initial', this);
30804     },
30805
30806     bind : function()
30807     {
30808         var _this = this;
30809         
30810         window.addEventListener("resize", function() { _this.resize(); } );
30811         
30812         this.bodyEl.on('click', this.beforeSelectFile, this);
30813         
30814         if(Roo.isTouch){
30815             this.bodyEl.on('touchstart', this.onTouchStart, this);
30816             this.bodyEl.on('touchmove', this.onTouchMove, this);
30817             this.bodyEl.on('touchend', this.onTouchEnd, this);
30818         }
30819         
30820         if(!Roo.isTouch){
30821             this.bodyEl.on('mousedown', this.onMouseDown, this);
30822             this.bodyEl.on('mousemove', this.onMouseMove, this);
30823             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30824             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30825             Roo.get(document).on('mouseup', this.onMouseUp, this);
30826         }
30827         
30828         this.selectorEl.on('change', this.onFileSelected, this);
30829     },
30830     
30831     reset : function()
30832     {    
30833         this.scale = 0;
30834         this.baseScale = 1;
30835         this.rotate = 0;
30836         this.baseRotate = 1;
30837         this.dragable = false;
30838         this.pinching = false;
30839         this.mouseX = 0;
30840         this.mouseY = 0;
30841         this.cropData = false;
30842         this.notifyEl.dom.innerHTML = this.emptyText;
30843         
30844         this.selectorEl.dom.value = '';
30845         
30846     },
30847     
30848     resize : function()
30849     {
30850         if(this.fireEvent('resize', this) != false){
30851             this.setThumbBoxPosition();
30852             this.setCanvasPosition();
30853         }
30854     },
30855     
30856     onFooterButtonClick : function(e, el, o, type)
30857     {
30858         switch (type) {
30859             case 'rotate-left' :
30860                 this.onRotateLeft(e);
30861                 break;
30862             case 'rotate-right' :
30863                 this.onRotateRight(e);
30864                 break;
30865             case 'picture' :
30866                 this.beforeSelectFile(e);
30867                 break;
30868             case 'trash' :
30869                 this.trash(e);
30870                 break;
30871             case 'crop' :
30872                 this.crop(e);
30873                 break;
30874             case 'download' :
30875                 this.download(e);
30876                 break;
30877             default :
30878                 break;
30879         }
30880         
30881         this.fireEvent('footerbuttonclick', this, type);
30882     },
30883     
30884     beforeSelectFile : function(e)
30885     {
30886         e.preventDefault();
30887         
30888         if(this.fireEvent('beforeselectfile', this) != false){
30889             this.selectorEl.dom.click();
30890         }
30891     },
30892     
30893     onFileSelected : function(e)
30894     {
30895         e.preventDefault();
30896         
30897         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30898             return;
30899         }
30900         
30901         var file = this.selectorEl.dom.files[0];
30902         
30903         if(this.fireEvent('inspect', this, file) != false){
30904             this.prepare(file);
30905         }
30906         
30907     },
30908     
30909     trash : function(e)
30910     {
30911         this.fireEvent('trash', this);
30912     },
30913     
30914     download : function(e)
30915     {
30916         this.fireEvent('download', this);
30917     },
30918     
30919     loadCanvas : function(src)
30920     {   
30921         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30922             
30923             this.reset();
30924             
30925             this.imageEl = document.createElement('img');
30926             
30927             var _this = this;
30928             
30929             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30930             
30931             this.imageEl.src = src;
30932         }
30933     },
30934     
30935     onLoadCanvas : function()
30936     {   
30937         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30938         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30939         
30940         this.bodyEl.un('click', this.beforeSelectFile, this);
30941         
30942         this.notifyEl.hide();
30943         this.thumbEl.show();
30944         this.footerEl.show();
30945         
30946         this.baseRotateLevel();
30947         
30948         if(this.isDocument){
30949             this.setThumbBoxSize();
30950         }
30951         
30952         this.setThumbBoxPosition();
30953         
30954         this.baseScaleLevel();
30955         
30956         this.draw();
30957         
30958         this.resize();
30959         
30960         this.canvasLoaded = true;
30961         
30962         if(this.loadMask){
30963             this.maskEl.unmask();
30964         }
30965         
30966     },
30967     
30968     setCanvasPosition : function()
30969     {   
30970         if(!this.canvasEl){
30971             return;
30972         }
30973         
30974         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30975         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30976         
30977         this.previewEl.setLeft(pw);
30978         this.previewEl.setTop(ph);
30979         
30980     },
30981     
30982     onMouseDown : function(e)
30983     {   
30984         e.stopEvent();
30985         
30986         this.dragable = true;
30987         this.pinching = false;
30988         
30989         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30990             this.dragable = false;
30991             return;
30992         }
30993         
30994         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30995         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30996         
30997     },
30998     
30999     onMouseMove : function(e)
31000     {   
31001         e.stopEvent();
31002         
31003         if(!this.canvasLoaded){
31004             return;
31005         }
31006         
31007         if (!this.dragable){
31008             return;
31009         }
31010         
31011         var minX = Math.ceil(this.thumbEl.getLeft(true));
31012         var minY = Math.ceil(this.thumbEl.getTop(true));
31013         
31014         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31015         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31016         
31017         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31018         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31019         
31020         x = x - this.mouseX;
31021         y = y - this.mouseY;
31022         
31023         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31024         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31025         
31026         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31027         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31028         
31029         this.previewEl.setLeft(bgX);
31030         this.previewEl.setTop(bgY);
31031         
31032         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31033         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31034     },
31035     
31036     onMouseUp : function(e)
31037     {   
31038         e.stopEvent();
31039         
31040         this.dragable = false;
31041     },
31042     
31043     onMouseWheel : function(e)
31044     {   
31045         e.stopEvent();
31046         
31047         this.startScale = this.scale;
31048         
31049         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31050         
31051         if(!this.zoomable()){
31052             this.scale = this.startScale;
31053             return;
31054         }
31055         
31056         this.draw();
31057         
31058         return;
31059     },
31060     
31061     zoomable : function()
31062     {
31063         var minScale = this.thumbEl.getWidth() / this.minWidth;
31064         
31065         if(this.minWidth < this.minHeight){
31066             minScale = this.thumbEl.getHeight() / this.minHeight;
31067         }
31068         
31069         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31070         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31071         
31072         if(
31073                 this.isDocument &&
31074                 (this.rotate == 0 || this.rotate == 180) && 
31075                 (
31076                     width > this.imageEl.OriginWidth || 
31077                     height > this.imageEl.OriginHeight ||
31078                     (width < this.minWidth && height < this.minHeight)
31079                 )
31080         ){
31081             return false;
31082         }
31083         
31084         if(
31085                 this.isDocument &&
31086                 (this.rotate == 90 || this.rotate == 270) && 
31087                 (
31088                     width > this.imageEl.OriginWidth || 
31089                     height > this.imageEl.OriginHeight ||
31090                     (width < this.minHeight && height < this.minWidth)
31091                 )
31092         ){
31093             return false;
31094         }
31095         
31096         if(
31097                 !this.isDocument &&
31098                 (this.rotate == 0 || this.rotate == 180) && 
31099                 (
31100                     width < this.minWidth || 
31101                     width > this.imageEl.OriginWidth || 
31102                     height < this.minHeight || 
31103                     height > this.imageEl.OriginHeight
31104                 )
31105         ){
31106             return false;
31107         }
31108         
31109         if(
31110                 !this.isDocument &&
31111                 (this.rotate == 90 || this.rotate == 270) && 
31112                 (
31113                     width < this.minHeight || 
31114                     width > this.imageEl.OriginWidth || 
31115                     height < this.minWidth || 
31116                     height > this.imageEl.OriginHeight
31117                 )
31118         ){
31119             return false;
31120         }
31121         
31122         return true;
31123         
31124     },
31125     
31126     onRotateLeft : function(e)
31127     {   
31128         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31129             
31130             var minScale = this.thumbEl.getWidth() / this.minWidth;
31131             
31132             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31133             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31134             
31135             this.startScale = this.scale;
31136             
31137             while (this.getScaleLevel() < minScale){
31138             
31139                 this.scale = this.scale + 1;
31140                 
31141                 if(!this.zoomable()){
31142                     break;
31143                 }
31144                 
31145                 if(
31146                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31147                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31148                 ){
31149                     continue;
31150                 }
31151                 
31152                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31153
31154                 this.draw();
31155                 
31156                 return;
31157             }
31158             
31159             this.scale = this.startScale;
31160             
31161             this.onRotateFail();
31162             
31163             return false;
31164         }
31165         
31166         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31167
31168         if(this.isDocument){
31169             this.setThumbBoxSize();
31170             this.setThumbBoxPosition();
31171             this.setCanvasPosition();
31172         }
31173         
31174         this.draw();
31175         
31176         this.fireEvent('rotate', this, 'left');
31177         
31178     },
31179     
31180     onRotateRight : function(e)
31181     {
31182         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31183             
31184             var minScale = this.thumbEl.getWidth() / this.minWidth;
31185         
31186             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31187             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31188             
31189             this.startScale = this.scale;
31190             
31191             while (this.getScaleLevel() < minScale){
31192             
31193                 this.scale = this.scale + 1;
31194                 
31195                 if(!this.zoomable()){
31196                     break;
31197                 }
31198                 
31199                 if(
31200                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31201                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31202                 ){
31203                     continue;
31204                 }
31205                 
31206                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31207
31208                 this.draw();
31209                 
31210                 return;
31211             }
31212             
31213             this.scale = this.startScale;
31214             
31215             this.onRotateFail();
31216             
31217             return false;
31218         }
31219         
31220         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31221
31222         if(this.isDocument){
31223             this.setThumbBoxSize();
31224             this.setThumbBoxPosition();
31225             this.setCanvasPosition();
31226         }
31227         
31228         this.draw();
31229         
31230         this.fireEvent('rotate', this, 'right');
31231     },
31232     
31233     onRotateFail : function()
31234     {
31235         this.errorEl.show(true);
31236         
31237         var _this = this;
31238         
31239         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31240     },
31241     
31242     draw : function()
31243     {
31244         this.previewEl.dom.innerHTML = '';
31245         
31246         var canvasEl = document.createElement("canvas");
31247         
31248         var contextEl = canvasEl.getContext("2d");
31249         
31250         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31251         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31252         var center = this.imageEl.OriginWidth / 2;
31253         
31254         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31255             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31256             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31257             center = this.imageEl.OriginHeight / 2;
31258         }
31259         
31260         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31261         
31262         contextEl.translate(center, center);
31263         contextEl.rotate(this.rotate * Math.PI / 180);
31264
31265         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31266         
31267         this.canvasEl = document.createElement("canvas");
31268         
31269         this.contextEl = this.canvasEl.getContext("2d");
31270         
31271         switch (this.rotate) {
31272             case 0 :
31273                 
31274                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31275                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31276                 
31277                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31278                 
31279                 break;
31280             case 90 : 
31281                 
31282                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31283                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31284                 
31285                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31286                     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);
31287                     break;
31288                 }
31289                 
31290                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31291                 
31292                 break;
31293             case 180 :
31294                 
31295                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31296                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31297                 
31298                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31299                     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);
31300                     break;
31301                 }
31302                 
31303                 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);
31304                 
31305                 break;
31306             case 270 :
31307                 
31308                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31309                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31310         
31311                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31312                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31313                     break;
31314                 }
31315                 
31316                 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);
31317                 
31318                 break;
31319             default : 
31320                 break;
31321         }
31322         
31323         this.previewEl.appendChild(this.canvasEl);
31324         
31325         this.setCanvasPosition();
31326     },
31327     
31328     crop : function()
31329     {
31330         if(!this.canvasLoaded){
31331             return;
31332         }
31333         
31334         var imageCanvas = document.createElement("canvas");
31335         
31336         var imageContext = imageCanvas.getContext("2d");
31337         
31338         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31339         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31340         
31341         var center = imageCanvas.width / 2;
31342         
31343         imageContext.translate(center, center);
31344         
31345         imageContext.rotate(this.rotate * Math.PI / 180);
31346         
31347         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31348         
31349         var canvas = document.createElement("canvas");
31350         
31351         var context = canvas.getContext("2d");
31352                 
31353         canvas.width = this.minWidth;
31354         canvas.height = this.minHeight;
31355
31356         switch (this.rotate) {
31357             case 0 :
31358                 
31359                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31360                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31361                 
31362                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31363                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31364                 
31365                 var targetWidth = this.minWidth - 2 * x;
31366                 var targetHeight = this.minHeight - 2 * y;
31367                 
31368                 var scale = 1;
31369                 
31370                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31371                     scale = targetWidth / width;
31372                 }
31373                 
31374                 if(x > 0 && y == 0){
31375                     scale = targetHeight / height;
31376                 }
31377                 
31378                 if(x > 0 && y > 0){
31379                     scale = targetWidth / width;
31380                     
31381                     if(width < height){
31382                         scale = targetHeight / height;
31383                     }
31384                 }
31385                 
31386                 context.scale(scale, scale);
31387                 
31388                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31389                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31390
31391                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31392                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31393
31394                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31395                 
31396                 break;
31397             case 90 : 
31398                 
31399                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31400                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31401                 
31402                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31403                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31404                 
31405                 var targetWidth = this.minWidth - 2 * x;
31406                 var targetHeight = this.minHeight - 2 * y;
31407                 
31408                 var scale = 1;
31409                 
31410                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31411                     scale = targetWidth / width;
31412                 }
31413                 
31414                 if(x > 0 && y == 0){
31415                     scale = targetHeight / height;
31416                 }
31417                 
31418                 if(x > 0 && y > 0){
31419                     scale = targetWidth / width;
31420                     
31421                     if(width < height){
31422                         scale = targetHeight / height;
31423                     }
31424                 }
31425                 
31426                 context.scale(scale, scale);
31427                 
31428                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31429                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31430
31431                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31432                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31433                 
31434                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31435                 
31436                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31437                 
31438                 break;
31439             case 180 :
31440                 
31441                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31442                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31443                 
31444                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31445                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31446                 
31447                 var targetWidth = this.minWidth - 2 * x;
31448                 var targetHeight = this.minHeight - 2 * y;
31449                 
31450                 var scale = 1;
31451                 
31452                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31453                     scale = targetWidth / width;
31454                 }
31455                 
31456                 if(x > 0 && y == 0){
31457                     scale = targetHeight / height;
31458                 }
31459                 
31460                 if(x > 0 && y > 0){
31461                     scale = targetWidth / width;
31462                     
31463                     if(width < height){
31464                         scale = targetHeight / height;
31465                     }
31466                 }
31467                 
31468                 context.scale(scale, scale);
31469                 
31470                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31471                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31472
31473                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31474                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31475
31476                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31477                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31478                 
31479                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31480                 
31481                 break;
31482             case 270 :
31483                 
31484                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31485                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31486                 
31487                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31488                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31489                 
31490                 var targetWidth = this.minWidth - 2 * x;
31491                 var targetHeight = this.minHeight - 2 * y;
31492                 
31493                 var scale = 1;
31494                 
31495                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31496                     scale = targetWidth / width;
31497                 }
31498                 
31499                 if(x > 0 && y == 0){
31500                     scale = targetHeight / height;
31501                 }
31502                 
31503                 if(x > 0 && y > 0){
31504                     scale = targetWidth / width;
31505                     
31506                     if(width < height){
31507                         scale = targetHeight / height;
31508                     }
31509                 }
31510                 
31511                 context.scale(scale, scale);
31512                 
31513                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31514                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31515
31516                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31517                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31518                 
31519                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31520                 
31521                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31522                 
31523                 break;
31524             default : 
31525                 break;
31526         }
31527         
31528         this.cropData = canvas.toDataURL(this.cropType);
31529         
31530         if(this.fireEvent('crop', this, this.cropData) !== false){
31531             this.process(this.file, this.cropData);
31532         }
31533         
31534         return;
31535         
31536     },
31537     
31538     setThumbBoxSize : function()
31539     {
31540         var width, height;
31541         
31542         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31543             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31544             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31545             
31546             this.minWidth = width;
31547             this.minHeight = height;
31548             
31549             if(this.rotate == 90 || this.rotate == 270){
31550                 this.minWidth = height;
31551                 this.minHeight = width;
31552             }
31553         }
31554         
31555         height = 300;
31556         width = Math.ceil(this.minWidth * height / this.minHeight);
31557         
31558         if(this.minWidth > this.minHeight){
31559             width = 300;
31560             height = Math.ceil(this.minHeight * width / this.minWidth);
31561         }
31562         
31563         this.thumbEl.setStyle({
31564             width : width + 'px',
31565             height : height + 'px'
31566         });
31567
31568         return;
31569             
31570     },
31571     
31572     setThumbBoxPosition : function()
31573     {
31574         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31575         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31576         
31577         this.thumbEl.setLeft(x);
31578         this.thumbEl.setTop(y);
31579         
31580     },
31581     
31582     baseRotateLevel : function()
31583     {
31584         this.baseRotate = 1;
31585         
31586         if(
31587                 typeof(this.exif) != 'undefined' &&
31588                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31589                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31590         ){
31591             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31592         }
31593         
31594         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31595         
31596     },
31597     
31598     baseScaleLevel : function()
31599     {
31600         var width, height;
31601         
31602         if(this.isDocument){
31603             
31604             if(this.baseRotate == 6 || this.baseRotate == 8){
31605             
31606                 height = this.thumbEl.getHeight();
31607                 this.baseScale = height / this.imageEl.OriginWidth;
31608
31609                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31610                     width = this.thumbEl.getWidth();
31611                     this.baseScale = width / this.imageEl.OriginHeight;
31612                 }
31613
31614                 return;
31615             }
31616
31617             height = this.thumbEl.getHeight();
31618             this.baseScale = height / this.imageEl.OriginHeight;
31619
31620             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31621                 width = this.thumbEl.getWidth();
31622                 this.baseScale = width / this.imageEl.OriginWidth;
31623             }
31624
31625             return;
31626         }
31627         
31628         if(this.baseRotate == 6 || this.baseRotate == 8){
31629             
31630             width = this.thumbEl.getHeight();
31631             this.baseScale = width / this.imageEl.OriginHeight;
31632             
31633             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31634                 height = this.thumbEl.getWidth();
31635                 this.baseScale = height / this.imageEl.OriginHeight;
31636             }
31637             
31638             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31639                 height = this.thumbEl.getWidth();
31640                 this.baseScale = height / this.imageEl.OriginHeight;
31641                 
31642                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31643                     width = this.thumbEl.getHeight();
31644                     this.baseScale = width / this.imageEl.OriginWidth;
31645                 }
31646             }
31647             
31648             return;
31649         }
31650         
31651         width = this.thumbEl.getWidth();
31652         this.baseScale = width / this.imageEl.OriginWidth;
31653         
31654         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31655             height = this.thumbEl.getHeight();
31656             this.baseScale = height / this.imageEl.OriginHeight;
31657         }
31658         
31659         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31660             
31661             height = this.thumbEl.getHeight();
31662             this.baseScale = height / this.imageEl.OriginHeight;
31663             
31664             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31665                 width = this.thumbEl.getWidth();
31666                 this.baseScale = width / this.imageEl.OriginWidth;
31667             }
31668             
31669         }
31670         
31671         return;
31672     },
31673     
31674     getScaleLevel : function()
31675     {
31676         return this.baseScale * Math.pow(1.1, this.scale);
31677     },
31678     
31679     onTouchStart : function(e)
31680     {
31681         if(!this.canvasLoaded){
31682             this.beforeSelectFile(e);
31683             return;
31684         }
31685         
31686         var touches = e.browserEvent.touches;
31687         
31688         if(!touches){
31689             return;
31690         }
31691         
31692         if(touches.length == 1){
31693             this.onMouseDown(e);
31694             return;
31695         }
31696         
31697         if(touches.length != 2){
31698             return;
31699         }
31700         
31701         var coords = [];
31702         
31703         for(var i = 0, finger; finger = touches[i]; i++){
31704             coords.push(finger.pageX, finger.pageY);
31705         }
31706         
31707         var x = Math.pow(coords[0] - coords[2], 2);
31708         var y = Math.pow(coords[1] - coords[3], 2);
31709         
31710         this.startDistance = Math.sqrt(x + y);
31711         
31712         this.startScale = this.scale;
31713         
31714         this.pinching = true;
31715         this.dragable = false;
31716         
31717     },
31718     
31719     onTouchMove : function(e)
31720     {
31721         if(!this.pinching && !this.dragable){
31722             return;
31723         }
31724         
31725         var touches = e.browserEvent.touches;
31726         
31727         if(!touches){
31728             return;
31729         }
31730         
31731         if(this.dragable){
31732             this.onMouseMove(e);
31733             return;
31734         }
31735         
31736         var coords = [];
31737         
31738         for(var i = 0, finger; finger = touches[i]; i++){
31739             coords.push(finger.pageX, finger.pageY);
31740         }
31741         
31742         var x = Math.pow(coords[0] - coords[2], 2);
31743         var y = Math.pow(coords[1] - coords[3], 2);
31744         
31745         this.endDistance = Math.sqrt(x + y);
31746         
31747         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31748         
31749         if(!this.zoomable()){
31750             this.scale = this.startScale;
31751             return;
31752         }
31753         
31754         this.draw();
31755         
31756     },
31757     
31758     onTouchEnd : function(e)
31759     {
31760         this.pinching = false;
31761         this.dragable = false;
31762         
31763     },
31764     
31765     process : function(file, crop)
31766     {
31767         if(this.loadMask){
31768             this.maskEl.mask(this.loadingText);
31769         }
31770         
31771         this.xhr = new XMLHttpRequest();
31772         
31773         file.xhr = this.xhr;
31774
31775         this.xhr.open(this.method, this.url, true);
31776         
31777         var headers = {
31778             "Accept": "application/json",
31779             "Cache-Control": "no-cache",
31780             "X-Requested-With": "XMLHttpRequest"
31781         };
31782         
31783         for (var headerName in headers) {
31784             var headerValue = headers[headerName];
31785             if (headerValue) {
31786                 this.xhr.setRequestHeader(headerName, headerValue);
31787             }
31788         }
31789         
31790         var _this = this;
31791         
31792         this.xhr.onload = function()
31793         {
31794             _this.xhrOnLoad(_this.xhr);
31795         }
31796         
31797         this.xhr.onerror = function()
31798         {
31799             _this.xhrOnError(_this.xhr);
31800         }
31801         
31802         var formData = new FormData();
31803
31804         formData.append('returnHTML', 'NO');
31805         
31806         if(crop){
31807             formData.append('crop', crop);
31808         }
31809         
31810         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31811             formData.append(this.paramName, file, file.name);
31812         }
31813         
31814         if(typeof(file.filename) != 'undefined'){
31815             formData.append('filename', file.filename);
31816         }
31817         
31818         if(typeof(file.mimetype) != 'undefined'){
31819             formData.append('mimetype', file.mimetype);
31820         }
31821         
31822         if(this.fireEvent('arrange', this, formData) != false){
31823             this.xhr.send(formData);
31824         };
31825     },
31826     
31827     xhrOnLoad : function(xhr)
31828     {
31829         if(this.loadMask){
31830             this.maskEl.unmask();
31831         }
31832         
31833         if (xhr.readyState !== 4) {
31834             this.fireEvent('exception', this, xhr);
31835             return;
31836         }
31837
31838         var response = Roo.decode(xhr.responseText);
31839         
31840         if(!response.success){
31841             this.fireEvent('exception', this, xhr);
31842             return;
31843         }
31844         
31845         var response = Roo.decode(xhr.responseText);
31846         
31847         this.fireEvent('upload', this, response);
31848         
31849     },
31850     
31851     xhrOnError : function()
31852     {
31853         if(this.loadMask){
31854             this.maskEl.unmask();
31855         }
31856         
31857         Roo.log('xhr on error');
31858         
31859         var response = Roo.decode(xhr.responseText);
31860           
31861         Roo.log(response);
31862         
31863     },
31864     
31865     prepare : function(file)
31866     {   
31867         if(this.loadMask){
31868             this.maskEl.mask(this.loadingText);
31869         }
31870         
31871         this.file = false;
31872         this.exif = {};
31873         
31874         if(typeof(file) === 'string'){
31875             this.loadCanvas(file);
31876             return;
31877         }
31878         
31879         if(!file || !this.urlAPI){
31880             return;
31881         }
31882         
31883         this.file = file;
31884         this.cropType = file.type;
31885         
31886         var _this = this;
31887         
31888         if(this.fireEvent('prepare', this, this.file) != false){
31889             
31890             var reader = new FileReader();
31891             
31892             reader.onload = function (e) {
31893                 if (e.target.error) {
31894                     Roo.log(e.target.error);
31895                     return;
31896                 }
31897                 
31898                 var buffer = e.target.result,
31899                     dataView = new DataView(buffer),
31900                     offset = 2,
31901                     maxOffset = dataView.byteLength - 4,
31902                     markerBytes,
31903                     markerLength;
31904                 
31905                 if (dataView.getUint16(0) === 0xffd8) {
31906                     while (offset < maxOffset) {
31907                         markerBytes = dataView.getUint16(offset);
31908                         
31909                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31910                             markerLength = dataView.getUint16(offset + 2) + 2;
31911                             if (offset + markerLength > dataView.byteLength) {
31912                                 Roo.log('Invalid meta data: Invalid segment size.');
31913                                 break;
31914                             }
31915                             
31916                             if(markerBytes == 0xffe1){
31917                                 _this.parseExifData(
31918                                     dataView,
31919                                     offset,
31920                                     markerLength
31921                                 );
31922                             }
31923                             
31924                             offset += markerLength;
31925                             
31926                             continue;
31927                         }
31928                         
31929                         break;
31930                     }
31931                     
31932                 }
31933                 
31934                 var url = _this.urlAPI.createObjectURL(_this.file);
31935                 
31936                 _this.loadCanvas(url);
31937                 
31938                 return;
31939             }
31940             
31941             reader.readAsArrayBuffer(this.file);
31942             
31943         }
31944         
31945     },
31946     
31947     parseExifData : function(dataView, offset, length)
31948     {
31949         var tiffOffset = offset + 10,
31950             littleEndian,
31951             dirOffset;
31952     
31953         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31954             // No Exif data, might be XMP data instead
31955             return;
31956         }
31957         
31958         // Check for the ASCII code for "Exif" (0x45786966):
31959         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31960             // No Exif data, might be XMP data instead
31961             return;
31962         }
31963         if (tiffOffset + 8 > dataView.byteLength) {
31964             Roo.log('Invalid Exif data: Invalid segment size.');
31965             return;
31966         }
31967         // Check for the two null bytes:
31968         if (dataView.getUint16(offset + 8) !== 0x0000) {
31969             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31970             return;
31971         }
31972         // Check the byte alignment:
31973         switch (dataView.getUint16(tiffOffset)) {
31974         case 0x4949:
31975             littleEndian = true;
31976             break;
31977         case 0x4D4D:
31978             littleEndian = false;
31979             break;
31980         default:
31981             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31982             return;
31983         }
31984         // Check for the TIFF tag marker (0x002A):
31985         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31986             Roo.log('Invalid Exif data: Missing TIFF marker.');
31987             return;
31988         }
31989         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31990         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31991         
31992         this.parseExifTags(
31993             dataView,
31994             tiffOffset,
31995             tiffOffset + dirOffset,
31996             littleEndian
31997         );
31998     },
31999     
32000     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32001     {
32002         var tagsNumber,
32003             dirEndOffset,
32004             i;
32005         if (dirOffset + 6 > dataView.byteLength) {
32006             Roo.log('Invalid Exif data: Invalid directory offset.');
32007             return;
32008         }
32009         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32010         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32011         if (dirEndOffset + 4 > dataView.byteLength) {
32012             Roo.log('Invalid Exif data: Invalid directory size.');
32013             return;
32014         }
32015         for (i = 0; i < tagsNumber; i += 1) {
32016             this.parseExifTag(
32017                 dataView,
32018                 tiffOffset,
32019                 dirOffset + 2 + 12 * i, // tag offset
32020                 littleEndian
32021             );
32022         }
32023         // Return the offset to the next directory:
32024         return dataView.getUint32(dirEndOffset, littleEndian);
32025     },
32026     
32027     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32028     {
32029         var tag = dataView.getUint16(offset, littleEndian);
32030         
32031         this.exif[tag] = this.getExifValue(
32032             dataView,
32033             tiffOffset,
32034             offset,
32035             dataView.getUint16(offset + 2, littleEndian), // tag type
32036             dataView.getUint32(offset + 4, littleEndian), // tag length
32037             littleEndian
32038         );
32039     },
32040     
32041     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32042     {
32043         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32044             tagSize,
32045             dataOffset,
32046             values,
32047             i,
32048             str,
32049             c;
32050     
32051         if (!tagType) {
32052             Roo.log('Invalid Exif data: Invalid tag type.');
32053             return;
32054         }
32055         
32056         tagSize = tagType.size * length;
32057         // Determine if the value is contained in the dataOffset bytes,
32058         // or if the value at the dataOffset is a pointer to the actual data:
32059         dataOffset = tagSize > 4 ?
32060                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32061         if (dataOffset + tagSize > dataView.byteLength) {
32062             Roo.log('Invalid Exif data: Invalid data offset.');
32063             return;
32064         }
32065         if (length === 1) {
32066             return tagType.getValue(dataView, dataOffset, littleEndian);
32067         }
32068         values = [];
32069         for (i = 0; i < length; i += 1) {
32070             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32071         }
32072         
32073         if (tagType.ascii) {
32074             str = '';
32075             // Concatenate the chars:
32076             for (i = 0; i < values.length; i += 1) {
32077                 c = values[i];
32078                 // Ignore the terminating NULL byte(s):
32079                 if (c === '\u0000') {
32080                     break;
32081                 }
32082                 str += c;
32083             }
32084             return str;
32085         }
32086         return values;
32087     }
32088     
32089 });
32090
32091 Roo.apply(Roo.bootstrap.UploadCropbox, {
32092     tags : {
32093         'Orientation': 0x0112
32094     },
32095     
32096     Orientation: {
32097             1: 0, //'top-left',
32098 //            2: 'top-right',
32099             3: 180, //'bottom-right',
32100 //            4: 'bottom-left',
32101 //            5: 'left-top',
32102             6: 90, //'right-top',
32103 //            7: 'right-bottom',
32104             8: 270 //'left-bottom'
32105     },
32106     
32107     exifTagTypes : {
32108         // byte, 8-bit unsigned int:
32109         1: {
32110             getValue: function (dataView, dataOffset) {
32111                 return dataView.getUint8(dataOffset);
32112             },
32113             size: 1
32114         },
32115         // ascii, 8-bit byte:
32116         2: {
32117             getValue: function (dataView, dataOffset) {
32118                 return String.fromCharCode(dataView.getUint8(dataOffset));
32119             },
32120             size: 1,
32121             ascii: true
32122         },
32123         // short, 16 bit int:
32124         3: {
32125             getValue: function (dataView, dataOffset, littleEndian) {
32126                 return dataView.getUint16(dataOffset, littleEndian);
32127             },
32128             size: 2
32129         },
32130         // long, 32 bit int:
32131         4: {
32132             getValue: function (dataView, dataOffset, littleEndian) {
32133                 return dataView.getUint32(dataOffset, littleEndian);
32134             },
32135             size: 4
32136         },
32137         // rational = two long values, first is numerator, second is denominator:
32138         5: {
32139             getValue: function (dataView, dataOffset, littleEndian) {
32140                 return dataView.getUint32(dataOffset, littleEndian) /
32141                     dataView.getUint32(dataOffset + 4, littleEndian);
32142             },
32143             size: 8
32144         },
32145         // slong, 32 bit signed int:
32146         9: {
32147             getValue: function (dataView, dataOffset, littleEndian) {
32148                 return dataView.getInt32(dataOffset, littleEndian);
32149             },
32150             size: 4
32151         },
32152         // srational, two slongs, first is numerator, second is denominator:
32153         10: {
32154             getValue: function (dataView, dataOffset, littleEndian) {
32155                 return dataView.getInt32(dataOffset, littleEndian) /
32156                     dataView.getInt32(dataOffset + 4, littleEndian);
32157             },
32158             size: 8
32159         }
32160     },
32161     
32162     footer : {
32163         STANDARD : [
32164             {
32165                 tag : 'div',
32166                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32167                 action : 'rotate-left',
32168                 cn : [
32169                     {
32170                         tag : 'button',
32171                         cls : 'btn btn-default',
32172                         html : '<i class="fa fa-undo"></i>'
32173                     }
32174                 ]
32175             },
32176             {
32177                 tag : 'div',
32178                 cls : 'btn-group roo-upload-cropbox-picture',
32179                 action : 'picture',
32180                 cn : [
32181                     {
32182                         tag : 'button',
32183                         cls : 'btn btn-default',
32184                         html : '<i class="fa fa-picture-o"></i>'
32185                     }
32186                 ]
32187             },
32188             {
32189                 tag : 'div',
32190                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32191                 action : 'rotate-right',
32192                 cn : [
32193                     {
32194                         tag : 'button',
32195                         cls : 'btn btn-default',
32196                         html : '<i class="fa fa-repeat"></i>'
32197                     }
32198                 ]
32199             }
32200         ],
32201         DOCUMENT : [
32202             {
32203                 tag : 'div',
32204                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32205                 action : 'rotate-left',
32206                 cn : [
32207                     {
32208                         tag : 'button',
32209                         cls : 'btn btn-default',
32210                         html : '<i class="fa fa-undo"></i>'
32211                     }
32212                 ]
32213             },
32214             {
32215                 tag : 'div',
32216                 cls : 'btn-group roo-upload-cropbox-download',
32217                 action : 'download',
32218                 cn : [
32219                     {
32220                         tag : 'button',
32221                         cls : 'btn btn-default',
32222                         html : '<i class="fa fa-download"></i>'
32223                     }
32224                 ]
32225             },
32226             {
32227                 tag : 'div',
32228                 cls : 'btn-group roo-upload-cropbox-crop',
32229                 action : 'crop',
32230                 cn : [
32231                     {
32232                         tag : 'button',
32233                         cls : 'btn btn-default',
32234                         html : '<i class="fa fa-crop"></i>'
32235                     }
32236                 ]
32237             },
32238             {
32239                 tag : 'div',
32240                 cls : 'btn-group roo-upload-cropbox-trash',
32241                 action : 'trash',
32242                 cn : [
32243                     {
32244                         tag : 'button',
32245                         cls : 'btn btn-default',
32246                         html : '<i class="fa fa-trash"></i>'
32247                     }
32248                 ]
32249             },
32250             {
32251                 tag : 'div',
32252                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32253                 action : 'rotate-right',
32254                 cn : [
32255                     {
32256                         tag : 'button',
32257                         cls : 'btn btn-default',
32258                         html : '<i class="fa fa-repeat"></i>'
32259                     }
32260                 ]
32261             }
32262         ],
32263         ROTATOR : [
32264             {
32265                 tag : 'div',
32266                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32267                 action : 'rotate-left',
32268                 cn : [
32269                     {
32270                         tag : 'button',
32271                         cls : 'btn btn-default',
32272                         html : '<i class="fa fa-undo"></i>'
32273                     }
32274                 ]
32275             },
32276             {
32277                 tag : 'div',
32278                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32279                 action : 'rotate-right',
32280                 cn : [
32281                     {
32282                         tag : 'button',
32283                         cls : 'btn btn-default',
32284                         html : '<i class="fa fa-repeat"></i>'
32285                     }
32286                 ]
32287             }
32288         ]
32289     }
32290 });
32291
32292 /*
32293 * Licence: LGPL
32294 */
32295
32296 /**
32297  * @class Roo.bootstrap.DocumentManager
32298  * @extends Roo.bootstrap.Component
32299  * Bootstrap DocumentManager class
32300  * @cfg {String} paramName default 'imageUpload'
32301  * @cfg {String} toolTipName default 'filename'
32302  * @cfg {String} method default POST
32303  * @cfg {String} url action url
32304  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32305  * @cfg {Boolean} multiple multiple upload default true
32306  * @cfg {Number} thumbSize default 300
32307  * @cfg {String} fieldLabel
32308  * @cfg {Number} labelWidth default 4
32309  * @cfg {String} labelAlign (left|top) default left
32310  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32311 * @cfg {Number} labellg set the width of label (1-12)
32312  * @cfg {Number} labelmd set the width of label (1-12)
32313  * @cfg {Number} labelsm set the width of label (1-12)
32314  * @cfg {Number} labelxs set the width of label (1-12)
32315  * 
32316  * @constructor
32317  * Create a new DocumentManager
32318  * @param {Object} config The config object
32319  */
32320
32321 Roo.bootstrap.DocumentManager = function(config){
32322     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32323     
32324     this.files = [];
32325     this.delegates = [];
32326     
32327     this.addEvents({
32328         /**
32329          * @event initial
32330          * Fire when initial the DocumentManager
32331          * @param {Roo.bootstrap.DocumentManager} this
32332          */
32333         "initial" : true,
32334         /**
32335          * @event inspect
32336          * inspect selected file
32337          * @param {Roo.bootstrap.DocumentManager} this
32338          * @param {File} file
32339          */
32340         "inspect" : true,
32341         /**
32342          * @event exception
32343          * Fire when xhr load exception
32344          * @param {Roo.bootstrap.DocumentManager} this
32345          * @param {XMLHttpRequest} xhr
32346          */
32347         "exception" : true,
32348         /**
32349          * @event afterupload
32350          * Fire when xhr load exception
32351          * @param {Roo.bootstrap.DocumentManager} this
32352          * @param {XMLHttpRequest} xhr
32353          */
32354         "afterupload" : true,
32355         /**
32356          * @event prepare
32357          * prepare the form data
32358          * @param {Roo.bootstrap.DocumentManager} this
32359          * @param {Object} formData
32360          */
32361         "prepare" : true,
32362         /**
32363          * @event remove
32364          * Fire when remove the file
32365          * @param {Roo.bootstrap.DocumentManager} this
32366          * @param {Object} file
32367          */
32368         "remove" : true,
32369         /**
32370          * @event refresh
32371          * Fire after refresh the file
32372          * @param {Roo.bootstrap.DocumentManager} this
32373          */
32374         "refresh" : true,
32375         /**
32376          * @event click
32377          * Fire after click the image
32378          * @param {Roo.bootstrap.DocumentManager} this
32379          * @param {Object} file
32380          */
32381         "click" : true,
32382         /**
32383          * @event edit
32384          * Fire when upload a image and editable set to true
32385          * @param {Roo.bootstrap.DocumentManager} this
32386          * @param {Object} file
32387          */
32388         "edit" : true,
32389         /**
32390          * @event beforeselectfile
32391          * Fire before select file
32392          * @param {Roo.bootstrap.DocumentManager} this
32393          */
32394         "beforeselectfile" : true,
32395         /**
32396          * @event process
32397          * Fire before process file
32398          * @param {Roo.bootstrap.DocumentManager} this
32399          * @param {Object} file
32400          */
32401         "process" : true,
32402         /**
32403          * @event previewrendered
32404          * Fire when preview rendered
32405          * @param {Roo.bootstrap.DocumentManager} this
32406          * @param {Object} file
32407          */
32408         "previewrendered" : true,
32409         /**
32410          */
32411         "previewResize" : true
32412         
32413     });
32414 };
32415
32416 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32417     
32418     boxes : 0,
32419     inputName : '',
32420     thumbSize : 300,
32421     multiple : true,
32422     files : false,
32423     method : 'POST',
32424     url : '',
32425     paramName : 'imageUpload',
32426     toolTipName : 'filename',
32427     fieldLabel : '',
32428     labelWidth : 4,
32429     labelAlign : 'left',
32430     editable : true,
32431     delegates : false,
32432     xhr : false, 
32433     
32434     labellg : 0,
32435     labelmd : 0,
32436     labelsm : 0,
32437     labelxs : 0,
32438     
32439     getAutoCreate : function()
32440     {   
32441         var managerWidget = {
32442             tag : 'div',
32443             cls : 'roo-document-manager',
32444             cn : [
32445                 {
32446                     tag : 'input',
32447                     cls : 'roo-document-manager-selector',
32448                     type : 'file'
32449                 },
32450                 {
32451                     tag : 'div',
32452                     cls : 'roo-document-manager-uploader',
32453                     cn : [
32454                         {
32455                             tag : 'div',
32456                             cls : 'roo-document-manager-upload-btn',
32457                             html : '<i class="fa fa-plus"></i>'
32458                         }
32459                     ]
32460                     
32461                 }
32462             ]
32463         };
32464         
32465         var content = [
32466             {
32467                 tag : 'div',
32468                 cls : 'column col-md-12',
32469                 cn : managerWidget
32470             }
32471         ];
32472         
32473         if(this.fieldLabel.length){
32474             
32475             content = [
32476                 {
32477                     tag : 'div',
32478                     cls : 'column col-md-12',
32479                     html : this.fieldLabel
32480                 },
32481                 {
32482                     tag : 'div',
32483                     cls : 'column col-md-12',
32484                     cn : managerWidget
32485                 }
32486             ];
32487
32488             if(this.labelAlign == 'left'){
32489                 content = [
32490                     {
32491                         tag : 'div',
32492                         cls : 'column',
32493                         html : this.fieldLabel
32494                     },
32495                     {
32496                         tag : 'div',
32497                         cls : 'column',
32498                         cn : managerWidget
32499                     }
32500                 ];
32501                 
32502                 if(this.labelWidth > 12){
32503                     content[0].style = "width: " + this.labelWidth + 'px';
32504                 }
32505
32506                 if(this.labelWidth < 13 && this.labelmd == 0){
32507                     this.labelmd = this.labelWidth;
32508                 }
32509
32510                 if(this.labellg > 0){
32511                     content[0].cls += ' col-lg-' + this.labellg;
32512                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32513                 }
32514
32515                 if(this.labelmd > 0){
32516                     content[0].cls += ' col-md-' + this.labelmd;
32517                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32518                 }
32519
32520                 if(this.labelsm > 0){
32521                     content[0].cls += ' col-sm-' + this.labelsm;
32522                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32523                 }
32524
32525                 if(this.labelxs > 0){
32526                     content[0].cls += ' col-xs-' + this.labelxs;
32527                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32528                 }
32529                 
32530             }
32531         }
32532         
32533         var cfg = {
32534             tag : 'div',
32535             cls : 'row clearfix',
32536             cn : content
32537         };
32538         
32539         return cfg;
32540         
32541     },
32542     
32543     initEvents : function()
32544     {
32545         this.managerEl = this.el.select('.roo-document-manager', true).first();
32546         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32547         
32548         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32549         this.selectorEl.hide();
32550         
32551         if(this.multiple){
32552             this.selectorEl.attr('multiple', 'multiple');
32553         }
32554         
32555         this.selectorEl.on('change', this.onFileSelected, this);
32556         
32557         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32558         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32559         
32560         this.uploader.on('click', this.onUploaderClick, this);
32561         
32562         this.renderProgressDialog();
32563         
32564         var _this = this;
32565         
32566         window.addEventListener("resize", function() { _this.refresh(); } );
32567         
32568         this.fireEvent('initial', this);
32569     },
32570     
32571     renderProgressDialog : function()
32572     {
32573         var _this = this;
32574         
32575         this.progressDialog = new Roo.bootstrap.Modal({
32576             cls : 'roo-document-manager-progress-dialog',
32577             allow_close : false,
32578             animate : false,
32579             title : '',
32580             buttons : [
32581                 {
32582                     name  :'cancel',
32583                     weight : 'danger',
32584                     html : 'Cancel'
32585                 }
32586             ], 
32587             listeners : { 
32588                 btnclick : function() {
32589                     _this.uploadCancel();
32590                     this.hide();
32591                 }
32592             }
32593         });
32594          
32595         this.progressDialog.render(Roo.get(document.body));
32596          
32597         this.progress = new Roo.bootstrap.Progress({
32598             cls : 'roo-document-manager-progress',
32599             active : true,
32600             striped : true
32601         });
32602         
32603         this.progress.render(this.progressDialog.getChildContainer());
32604         
32605         this.progressBar = new Roo.bootstrap.ProgressBar({
32606             cls : 'roo-document-manager-progress-bar',
32607             aria_valuenow : 0,
32608             aria_valuemin : 0,
32609             aria_valuemax : 12,
32610             panel : 'success'
32611         });
32612         
32613         this.progressBar.render(this.progress.getChildContainer());
32614     },
32615     
32616     onUploaderClick : function(e)
32617     {
32618         e.preventDefault();
32619      
32620         if(this.fireEvent('beforeselectfile', this) != false){
32621             this.selectorEl.dom.click();
32622         }
32623         
32624     },
32625     
32626     onFileSelected : function(e)
32627     {
32628         e.preventDefault();
32629         
32630         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32631             return;
32632         }
32633         
32634         Roo.each(this.selectorEl.dom.files, function(file){
32635             if(this.fireEvent('inspect', this, file) != false){
32636                 this.files.push(file);
32637             }
32638         }, this);
32639         
32640         this.queue();
32641         
32642     },
32643     
32644     queue : function()
32645     {
32646         this.selectorEl.dom.value = '';
32647         
32648         if(!this.files || !this.files.length){
32649             return;
32650         }
32651         
32652         if(this.boxes > 0 && this.files.length > this.boxes){
32653             this.files = this.files.slice(0, this.boxes);
32654         }
32655         
32656         this.uploader.show();
32657         
32658         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32659             this.uploader.hide();
32660         }
32661         
32662         var _this = this;
32663         
32664         var files = [];
32665         
32666         var docs = [];
32667         
32668         Roo.each(this.files, function(file){
32669             
32670             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32671                 var f = this.renderPreview(file);
32672                 files.push(f);
32673                 return;
32674             }
32675             
32676             if(file.type.indexOf('image') != -1){
32677                 this.delegates.push(
32678                     (function(){
32679                         _this.process(file);
32680                     }).createDelegate(this)
32681                 );
32682         
32683                 return;
32684             }
32685             
32686             docs.push(
32687                 (function(){
32688                     _this.process(file);
32689                 }).createDelegate(this)
32690             );
32691             
32692         }, this);
32693         
32694         this.files = files;
32695         
32696         this.delegates = this.delegates.concat(docs);
32697         
32698         if(!this.delegates.length){
32699             this.refresh();
32700             return;
32701         }
32702         
32703         this.progressBar.aria_valuemax = this.delegates.length;
32704         
32705         this.arrange();
32706         
32707         return;
32708     },
32709     
32710     arrange : function()
32711     {
32712         if(!this.delegates.length){
32713             this.progressDialog.hide();
32714             this.refresh();
32715             return;
32716         }
32717         
32718         var delegate = this.delegates.shift();
32719         
32720         this.progressDialog.show();
32721         
32722         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32723         
32724         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32725         
32726         delegate();
32727     },
32728     
32729     refresh : function()
32730     {
32731         this.uploader.show();
32732         
32733         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32734             this.uploader.hide();
32735         }
32736         
32737         Roo.isTouch ? this.closable(false) : this.closable(true);
32738         
32739         this.fireEvent('refresh', this);
32740     },
32741     
32742     onRemove : function(e, el, o)
32743     {
32744         e.preventDefault();
32745         
32746         this.fireEvent('remove', this, o);
32747         
32748     },
32749     
32750     remove : function(o)
32751     {
32752         var files = [];
32753         
32754         Roo.each(this.files, function(file){
32755             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32756                 files.push(file);
32757                 return;
32758             }
32759
32760             o.target.remove();
32761
32762         }, this);
32763         
32764         this.files = files;
32765         
32766         this.refresh();
32767     },
32768     
32769     clear : function()
32770     {
32771         Roo.each(this.files, function(file){
32772             if(!file.target){
32773                 return;
32774             }
32775             
32776             file.target.remove();
32777
32778         }, this);
32779         
32780         this.files = [];
32781         
32782         this.refresh();
32783     },
32784     
32785     onClick : function(e, el, o)
32786     {
32787         e.preventDefault();
32788         
32789         this.fireEvent('click', this, o);
32790         
32791     },
32792     
32793     closable : function(closable)
32794     {
32795         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32796             
32797             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32798             
32799             if(closable){
32800                 el.show();
32801                 return;
32802             }
32803             
32804             el.hide();
32805             
32806         }, this);
32807     },
32808     
32809     xhrOnLoad : function(xhr)
32810     {
32811         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32812             el.remove();
32813         }, this);
32814         
32815         if (xhr.readyState !== 4) {
32816             this.arrange();
32817             this.fireEvent('exception', this, xhr);
32818             return;
32819         }
32820
32821         var response = Roo.decode(xhr.responseText);
32822         
32823         if(!response.success){
32824             this.arrange();
32825             this.fireEvent('exception', this, xhr);
32826             return;
32827         }
32828         
32829         var file = this.renderPreview(response.data);
32830         
32831         this.files.push(file);
32832         
32833         this.arrange();
32834         
32835         this.fireEvent('afterupload', this, xhr);
32836         
32837     },
32838     
32839     xhrOnError : function(xhr)
32840     {
32841         Roo.log('xhr on error');
32842         
32843         var response = Roo.decode(xhr.responseText);
32844           
32845         Roo.log(response);
32846         
32847         this.arrange();
32848     },
32849     
32850     process : function(file)
32851     {
32852         if(this.fireEvent('process', this, file) !== false){
32853             if(this.editable && file.type.indexOf('image') != -1){
32854                 this.fireEvent('edit', this, file);
32855                 return;
32856             }
32857
32858             this.uploadStart(file, false);
32859
32860             return;
32861         }
32862         
32863     },
32864     
32865     uploadStart : function(file, crop)
32866     {
32867         this.xhr = new XMLHttpRequest();
32868         
32869         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32870             this.arrange();
32871             return;
32872         }
32873         
32874         file.xhr = this.xhr;
32875             
32876         this.managerEl.createChild({
32877             tag : 'div',
32878             cls : 'roo-document-manager-loading',
32879             cn : [
32880                 {
32881                     tag : 'div',
32882                     tooltip : file.name,
32883                     cls : 'roo-document-manager-thumb',
32884                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32885                 }
32886             ]
32887
32888         });
32889
32890         this.xhr.open(this.method, this.url, true);
32891         
32892         var headers = {
32893             "Accept": "application/json",
32894             "Cache-Control": "no-cache",
32895             "X-Requested-With": "XMLHttpRequest"
32896         };
32897         
32898         for (var headerName in headers) {
32899             var headerValue = headers[headerName];
32900             if (headerValue) {
32901                 this.xhr.setRequestHeader(headerName, headerValue);
32902             }
32903         }
32904         
32905         var _this = this;
32906         
32907         this.xhr.onload = function()
32908         {
32909             _this.xhrOnLoad(_this.xhr);
32910         }
32911         
32912         this.xhr.onerror = function()
32913         {
32914             _this.xhrOnError(_this.xhr);
32915         }
32916         
32917         var formData = new FormData();
32918
32919         formData.append('returnHTML', 'NO');
32920         
32921         if(crop){
32922             formData.append('crop', crop);
32923         }
32924         
32925         formData.append(this.paramName, file, file.name);
32926         
32927         var options = {
32928             file : file, 
32929             manually : false
32930         };
32931         
32932         if(this.fireEvent('prepare', this, formData, options) != false){
32933             
32934             if(options.manually){
32935                 return;
32936             }
32937             
32938             this.xhr.send(formData);
32939             return;
32940         };
32941         
32942         this.uploadCancel();
32943     },
32944     
32945     uploadCancel : function()
32946     {
32947         if (this.xhr) {
32948             this.xhr.abort();
32949         }
32950         
32951         this.delegates = [];
32952         
32953         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32954             el.remove();
32955         }, this);
32956         
32957         this.arrange();
32958     },
32959     
32960     renderPreview : function(file)
32961     {
32962         if(typeof(file.target) != 'undefined' && file.target){
32963             return file;
32964         }
32965         
32966         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32967         
32968         var previewEl = this.managerEl.createChild({
32969             tag : 'div',
32970             cls : 'roo-document-manager-preview',
32971             cn : [
32972                 {
32973                     tag : 'div',
32974                     tooltip : file[this.toolTipName],
32975                     cls : 'roo-document-manager-thumb',
32976                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32977                 },
32978                 {
32979                     tag : 'button',
32980                     cls : 'close',
32981                     html : '<i class="fa fa-times-circle"></i>'
32982                 }
32983             ]
32984         });
32985
32986         var close = previewEl.select('button.close', true).first();
32987
32988         close.on('click', this.onRemove, this, file);
32989
32990         file.target = previewEl;
32991
32992         var image = previewEl.select('img', true).first();
32993         
32994         var _this = this;
32995         
32996         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32997         
32998         image.on('click', this.onClick, this, file);
32999         
33000         this.fireEvent('previewrendered', this, file);
33001         
33002         return file;
33003         
33004     },
33005     
33006     onPreviewLoad : function(file, image)
33007     {
33008         if(typeof(file.target) == 'undefined' || !file.target){
33009             return;
33010         }
33011         
33012         var width = image.dom.naturalWidth || image.dom.width;
33013         var height = image.dom.naturalHeight || image.dom.height;
33014         
33015         if(!this.previewResize) {
33016             return;
33017         }
33018         
33019         if(width > height){
33020             file.target.addClass('wide');
33021             return;
33022         }
33023         
33024         file.target.addClass('tall');
33025         return;
33026         
33027     },
33028     
33029     uploadFromSource : function(file, crop)
33030     {
33031         this.xhr = new XMLHttpRequest();
33032         
33033         this.managerEl.createChild({
33034             tag : 'div',
33035             cls : 'roo-document-manager-loading',
33036             cn : [
33037                 {
33038                     tag : 'div',
33039                     tooltip : file.name,
33040                     cls : 'roo-document-manager-thumb',
33041                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33042                 }
33043             ]
33044
33045         });
33046
33047         this.xhr.open(this.method, this.url, true);
33048         
33049         var headers = {
33050             "Accept": "application/json",
33051             "Cache-Control": "no-cache",
33052             "X-Requested-With": "XMLHttpRequest"
33053         };
33054         
33055         for (var headerName in headers) {
33056             var headerValue = headers[headerName];
33057             if (headerValue) {
33058                 this.xhr.setRequestHeader(headerName, headerValue);
33059             }
33060         }
33061         
33062         var _this = this;
33063         
33064         this.xhr.onload = function()
33065         {
33066             _this.xhrOnLoad(_this.xhr);
33067         }
33068         
33069         this.xhr.onerror = function()
33070         {
33071             _this.xhrOnError(_this.xhr);
33072         }
33073         
33074         var formData = new FormData();
33075
33076         formData.append('returnHTML', 'NO');
33077         
33078         formData.append('crop', crop);
33079         
33080         if(typeof(file.filename) != 'undefined'){
33081             formData.append('filename', file.filename);
33082         }
33083         
33084         if(typeof(file.mimetype) != 'undefined'){
33085             formData.append('mimetype', file.mimetype);
33086         }
33087         
33088         Roo.log(formData);
33089         
33090         if(this.fireEvent('prepare', this, formData) != false){
33091             this.xhr.send(formData);
33092         };
33093     }
33094 });
33095
33096 /*
33097 * Licence: LGPL
33098 */
33099
33100 /**
33101  * @class Roo.bootstrap.DocumentViewer
33102  * @extends Roo.bootstrap.Component
33103  * Bootstrap DocumentViewer class
33104  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33105  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33106  * 
33107  * @constructor
33108  * Create a new DocumentViewer
33109  * @param {Object} config The config object
33110  */
33111
33112 Roo.bootstrap.DocumentViewer = function(config){
33113     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33114     
33115     this.addEvents({
33116         /**
33117          * @event initial
33118          * Fire after initEvent
33119          * @param {Roo.bootstrap.DocumentViewer} this
33120          */
33121         "initial" : true,
33122         /**
33123          * @event click
33124          * Fire after click
33125          * @param {Roo.bootstrap.DocumentViewer} this
33126          */
33127         "click" : true,
33128         /**
33129          * @event download
33130          * Fire after download button
33131          * @param {Roo.bootstrap.DocumentViewer} this
33132          */
33133         "download" : true,
33134         /**
33135          * @event trash
33136          * Fire after trash button
33137          * @param {Roo.bootstrap.DocumentViewer} this
33138          */
33139         "trash" : true
33140         
33141     });
33142 };
33143
33144 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33145     
33146     showDownload : true,
33147     
33148     showTrash : true,
33149     
33150     getAutoCreate : function()
33151     {
33152         var cfg = {
33153             tag : 'div',
33154             cls : 'roo-document-viewer',
33155             cn : [
33156                 {
33157                     tag : 'div',
33158                     cls : 'roo-document-viewer-body',
33159                     cn : [
33160                         {
33161                             tag : 'div',
33162                             cls : 'roo-document-viewer-thumb',
33163                             cn : [
33164                                 {
33165                                     tag : 'img',
33166                                     cls : 'roo-document-viewer-image'
33167                                 }
33168                             ]
33169                         }
33170                     ]
33171                 },
33172                 {
33173                     tag : 'div',
33174                     cls : 'roo-document-viewer-footer',
33175                     cn : {
33176                         tag : 'div',
33177                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33178                         cn : [
33179                             {
33180                                 tag : 'div',
33181                                 cls : 'btn-group roo-document-viewer-download',
33182                                 cn : [
33183                                     {
33184                                         tag : 'button',
33185                                         cls : 'btn btn-default',
33186                                         html : '<i class="fa fa-download"></i>'
33187                                     }
33188                                 ]
33189                             },
33190                             {
33191                                 tag : 'div',
33192                                 cls : 'btn-group roo-document-viewer-trash',
33193                                 cn : [
33194                                     {
33195                                         tag : 'button',
33196                                         cls : 'btn btn-default',
33197                                         html : '<i class="fa fa-trash"></i>'
33198                                     }
33199                                 ]
33200                             }
33201                         ]
33202                     }
33203                 }
33204             ]
33205         };
33206         
33207         return cfg;
33208     },
33209     
33210     initEvents : function()
33211     {
33212         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33213         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33214         
33215         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33216         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33217         
33218         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33219         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33220         
33221         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33222         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33223         
33224         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33225         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33226         
33227         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33228         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33229         
33230         this.bodyEl.on('click', this.onClick, this);
33231         this.downloadBtn.on('click', this.onDownload, this);
33232         this.trashBtn.on('click', this.onTrash, this);
33233         
33234         this.downloadBtn.hide();
33235         this.trashBtn.hide();
33236         
33237         if(this.showDownload){
33238             this.downloadBtn.show();
33239         }
33240         
33241         if(this.showTrash){
33242             this.trashBtn.show();
33243         }
33244         
33245         if(!this.showDownload && !this.showTrash) {
33246             this.footerEl.hide();
33247         }
33248         
33249     },
33250     
33251     initial : function()
33252     {
33253         this.fireEvent('initial', this);
33254         
33255     },
33256     
33257     onClick : function(e)
33258     {
33259         e.preventDefault();
33260         
33261         this.fireEvent('click', this);
33262     },
33263     
33264     onDownload : function(e)
33265     {
33266         e.preventDefault();
33267         
33268         this.fireEvent('download', this);
33269     },
33270     
33271     onTrash : function(e)
33272     {
33273         e.preventDefault();
33274         
33275         this.fireEvent('trash', this);
33276     }
33277     
33278 });
33279 /*
33280  * - LGPL
33281  *
33282  * nav progress bar
33283  * 
33284  */
33285
33286 /**
33287  * @class Roo.bootstrap.NavProgressBar
33288  * @extends Roo.bootstrap.Component
33289  * Bootstrap NavProgressBar class
33290  * 
33291  * @constructor
33292  * Create a new nav progress bar
33293  * @param {Object} config The config object
33294  */
33295
33296 Roo.bootstrap.NavProgressBar = function(config){
33297     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33298
33299     this.bullets = this.bullets || [];
33300    
33301 //    Roo.bootstrap.NavProgressBar.register(this);
33302      this.addEvents({
33303         /**
33304              * @event changed
33305              * Fires when the active item changes
33306              * @param {Roo.bootstrap.NavProgressBar} this
33307              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33308              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33309          */
33310         'changed': true
33311      });
33312     
33313 };
33314
33315 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33316     
33317     bullets : [],
33318     barItems : [],
33319     
33320     getAutoCreate : function()
33321     {
33322         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33323         
33324         cfg = {
33325             tag : 'div',
33326             cls : 'roo-navigation-bar-group',
33327             cn : [
33328                 {
33329                     tag : 'div',
33330                     cls : 'roo-navigation-top-bar'
33331                 },
33332                 {
33333                     tag : 'div',
33334                     cls : 'roo-navigation-bullets-bar',
33335                     cn : [
33336                         {
33337                             tag : 'ul',
33338                             cls : 'roo-navigation-bar'
33339                         }
33340                     ]
33341                 },
33342                 
33343                 {
33344                     tag : 'div',
33345                     cls : 'roo-navigation-bottom-bar'
33346                 }
33347             ]
33348             
33349         };
33350         
33351         return cfg;
33352         
33353     },
33354     
33355     initEvents: function() 
33356     {
33357         
33358     },
33359     
33360     onRender : function(ct, position) 
33361     {
33362         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33363         
33364         if(this.bullets.length){
33365             Roo.each(this.bullets, function(b){
33366                this.addItem(b);
33367             }, this);
33368         }
33369         
33370         this.format();
33371         
33372     },
33373     
33374     addItem : function(cfg)
33375     {
33376         var item = new Roo.bootstrap.NavProgressItem(cfg);
33377         
33378         item.parentId = this.id;
33379         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33380         
33381         if(cfg.html){
33382             var top = new Roo.bootstrap.Element({
33383                 tag : 'div',
33384                 cls : 'roo-navigation-bar-text'
33385             });
33386             
33387             var bottom = new Roo.bootstrap.Element({
33388                 tag : 'div',
33389                 cls : 'roo-navigation-bar-text'
33390             });
33391             
33392             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33393             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33394             
33395             var topText = new Roo.bootstrap.Element({
33396                 tag : 'span',
33397                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33398             });
33399             
33400             var bottomText = new Roo.bootstrap.Element({
33401                 tag : 'span',
33402                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33403             });
33404             
33405             topText.onRender(top.el, null);
33406             bottomText.onRender(bottom.el, null);
33407             
33408             item.topEl = top;
33409             item.bottomEl = bottom;
33410         }
33411         
33412         this.barItems.push(item);
33413         
33414         return item;
33415     },
33416     
33417     getActive : function()
33418     {
33419         var active = false;
33420         
33421         Roo.each(this.barItems, function(v){
33422             
33423             if (!v.isActive()) {
33424                 return;
33425             }
33426             
33427             active = v;
33428             return false;
33429             
33430         });
33431         
33432         return active;
33433     },
33434     
33435     setActiveItem : function(item)
33436     {
33437         var prev = false;
33438         
33439         Roo.each(this.barItems, function(v){
33440             if (v.rid == item.rid) {
33441                 return ;
33442             }
33443             
33444             if (v.isActive()) {
33445                 v.setActive(false);
33446                 prev = v;
33447             }
33448         });
33449
33450         item.setActive(true);
33451         
33452         this.fireEvent('changed', this, item, prev);
33453     },
33454     
33455     getBarItem: function(rid)
33456     {
33457         var ret = false;
33458         
33459         Roo.each(this.barItems, function(e) {
33460             if (e.rid != rid) {
33461                 return;
33462             }
33463             
33464             ret =  e;
33465             return false;
33466         });
33467         
33468         return ret;
33469     },
33470     
33471     indexOfItem : function(item)
33472     {
33473         var index = false;
33474         
33475         Roo.each(this.barItems, function(v, i){
33476             
33477             if (v.rid != item.rid) {
33478                 return;
33479             }
33480             
33481             index = i;
33482             return false
33483         });
33484         
33485         return index;
33486     },
33487     
33488     setActiveNext : function()
33489     {
33490         var i = this.indexOfItem(this.getActive());
33491         
33492         if (i > this.barItems.length) {
33493             return;
33494         }
33495         
33496         this.setActiveItem(this.barItems[i+1]);
33497     },
33498     
33499     setActivePrev : function()
33500     {
33501         var i = this.indexOfItem(this.getActive());
33502         
33503         if (i  < 1) {
33504             return;
33505         }
33506         
33507         this.setActiveItem(this.barItems[i-1]);
33508     },
33509     
33510     format : function()
33511     {
33512         if(!this.barItems.length){
33513             return;
33514         }
33515      
33516         var width = 100 / this.barItems.length;
33517         
33518         Roo.each(this.barItems, function(i){
33519             i.el.setStyle('width', width + '%');
33520             i.topEl.el.setStyle('width', width + '%');
33521             i.bottomEl.el.setStyle('width', width + '%');
33522         }, this);
33523         
33524     }
33525     
33526 });
33527 /*
33528  * - LGPL
33529  *
33530  * Nav Progress Item
33531  * 
33532  */
33533
33534 /**
33535  * @class Roo.bootstrap.NavProgressItem
33536  * @extends Roo.bootstrap.Component
33537  * Bootstrap NavProgressItem class
33538  * @cfg {String} rid the reference id
33539  * @cfg {Boolean} active (true|false) Is item active default false
33540  * @cfg {Boolean} disabled (true|false) Is item active default false
33541  * @cfg {String} html
33542  * @cfg {String} position (top|bottom) text position default bottom
33543  * @cfg {String} icon show icon instead of number
33544  * 
33545  * @constructor
33546  * Create a new NavProgressItem
33547  * @param {Object} config The config object
33548  */
33549 Roo.bootstrap.NavProgressItem = function(config){
33550     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33551     this.addEvents({
33552         // raw events
33553         /**
33554          * @event click
33555          * The raw click event for the entire grid.
33556          * @param {Roo.bootstrap.NavProgressItem} this
33557          * @param {Roo.EventObject} e
33558          */
33559         "click" : true
33560     });
33561    
33562 };
33563
33564 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33565     
33566     rid : '',
33567     active : false,
33568     disabled : false,
33569     html : '',
33570     position : 'bottom',
33571     icon : false,
33572     
33573     getAutoCreate : function()
33574     {
33575         var iconCls = 'roo-navigation-bar-item-icon';
33576         
33577         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33578         
33579         var cfg = {
33580             tag: 'li',
33581             cls: 'roo-navigation-bar-item',
33582             cn : [
33583                 {
33584                     tag : 'i',
33585                     cls : iconCls
33586                 }
33587             ]
33588         };
33589         
33590         if(this.active){
33591             cfg.cls += ' active';
33592         }
33593         if(this.disabled){
33594             cfg.cls += ' disabled';
33595         }
33596         
33597         return cfg;
33598     },
33599     
33600     disable : function()
33601     {
33602         this.setDisabled(true);
33603     },
33604     
33605     enable : function()
33606     {
33607         this.setDisabled(false);
33608     },
33609     
33610     initEvents: function() 
33611     {
33612         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33613         
33614         this.iconEl.on('click', this.onClick, this);
33615     },
33616     
33617     onClick : function(e)
33618     {
33619         e.preventDefault();
33620         
33621         if(this.disabled){
33622             return;
33623         }
33624         
33625         if(this.fireEvent('click', this, e) === false){
33626             return;
33627         };
33628         
33629         this.parent().setActiveItem(this);
33630     },
33631     
33632     isActive: function () 
33633     {
33634         return this.active;
33635     },
33636     
33637     setActive : function(state)
33638     {
33639         if(this.active == state){
33640             return;
33641         }
33642         
33643         this.active = state;
33644         
33645         if (state) {
33646             this.el.addClass('active');
33647             return;
33648         }
33649         
33650         this.el.removeClass('active');
33651         
33652         return;
33653     },
33654     
33655     setDisabled : function(state)
33656     {
33657         if(this.disabled == state){
33658             return;
33659         }
33660         
33661         this.disabled = state;
33662         
33663         if (state) {
33664             this.el.addClass('disabled');
33665             return;
33666         }
33667         
33668         this.el.removeClass('disabled');
33669     },
33670     
33671     tooltipEl : function()
33672     {
33673         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33674     }
33675 });
33676  
33677
33678  /*
33679  * - LGPL
33680  *
33681  * FieldLabel
33682  * 
33683  */
33684
33685 /**
33686  * @class Roo.bootstrap.FieldLabel
33687  * @extends Roo.bootstrap.Component
33688  * Bootstrap FieldLabel class
33689  * @cfg {String} html contents of the element
33690  * @cfg {String} tag tag of the element default label
33691  * @cfg {String} cls class of the element
33692  * @cfg {String} target label target 
33693  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33694  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33695  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33696  * @cfg {String} iconTooltip default "This field is required"
33697  * @cfg {String} indicatorpos (left|right) default left
33698  * 
33699  * @constructor
33700  * Create a new FieldLabel
33701  * @param {Object} config The config object
33702  */
33703
33704 Roo.bootstrap.FieldLabel = function(config){
33705     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33706     
33707     this.addEvents({
33708             /**
33709              * @event invalid
33710              * Fires after the field has been marked as invalid.
33711              * @param {Roo.form.FieldLabel} this
33712              * @param {String} msg The validation message
33713              */
33714             invalid : true,
33715             /**
33716              * @event valid
33717              * Fires after the field has been validated with no errors.
33718              * @param {Roo.form.FieldLabel} this
33719              */
33720             valid : true
33721         });
33722 };
33723
33724 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33725     
33726     tag: 'label',
33727     cls: '',
33728     html: '',
33729     target: '',
33730     allowBlank : true,
33731     invalidClass : 'has-warning',
33732     validClass : 'has-success',
33733     iconTooltip : 'This field is required',
33734     indicatorpos : 'left',
33735     
33736     getAutoCreate : function(){
33737         
33738         var cls = "";
33739         if (!this.allowBlank) {
33740             cls  = "visible";
33741         }
33742         
33743         var cfg = {
33744             tag : this.tag,
33745             cls : 'roo-bootstrap-field-label ' + this.cls,
33746             for : this.target,
33747             cn : [
33748                 {
33749                     tag : 'i',
33750                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33751                     tooltip : this.iconTooltip
33752                 },
33753                 {
33754                     tag : 'span',
33755                     html : this.html
33756                 }
33757             ] 
33758         };
33759         
33760         if(this.indicatorpos == 'right'){
33761             var cfg = {
33762                 tag : this.tag,
33763                 cls : 'roo-bootstrap-field-label ' + this.cls,
33764                 for : this.target,
33765                 cn : [
33766                     {
33767                         tag : 'span',
33768                         html : this.html
33769                     },
33770                     {
33771                         tag : 'i',
33772                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33773                         tooltip : this.iconTooltip
33774                     }
33775                 ] 
33776             };
33777         }
33778         
33779         return cfg;
33780     },
33781     
33782     initEvents: function() 
33783     {
33784         Roo.bootstrap.Element.superclass.initEvents.call(this);
33785         
33786         this.indicator = this.indicatorEl();
33787         
33788         if(this.indicator){
33789             this.indicator.removeClass('visible');
33790             this.indicator.addClass('invisible');
33791         }
33792         
33793         Roo.bootstrap.FieldLabel.register(this);
33794     },
33795     
33796     indicatorEl : function()
33797     {
33798         var indicator = this.el.select('i.roo-required-indicator',true).first();
33799         
33800         if(!indicator){
33801             return false;
33802         }
33803         
33804         return indicator;
33805         
33806     },
33807     
33808     /**
33809      * Mark this field as valid
33810      */
33811     markValid : function()
33812     {
33813         if(this.indicator){
33814             this.indicator.removeClass('visible');
33815             this.indicator.addClass('invisible');
33816         }
33817         if (Roo.bootstrap.version == 3) {
33818             this.el.removeClass(this.invalidClass);
33819             this.el.addClass(this.validClass);
33820         } else {
33821             this.el.removeClass('is-invalid');
33822             this.el.addClass('is-valid');
33823         }
33824         
33825         
33826         this.fireEvent('valid', this);
33827     },
33828     
33829     /**
33830      * Mark this field as invalid
33831      * @param {String} msg The validation message
33832      */
33833     markInvalid : function(msg)
33834     {
33835         if(this.indicator){
33836             this.indicator.removeClass('invisible');
33837             this.indicator.addClass('visible');
33838         }
33839           if (Roo.bootstrap.version == 3) {
33840             this.el.removeClass(this.validClass);
33841             this.el.addClass(this.invalidClass);
33842         } else {
33843             this.el.removeClass('is-valid');
33844             this.el.addClass('is-invalid');
33845         }
33846         
33847         
33848         this.fireEvent('invalid', this, msg);
33849     }
33850     
33851    
33852 });
33853
33854 Roo.apply(Roo.bootstrap.FieldLabel, {
33855     
33856     groups: {},
33857     
33858      /**
33859     * register a FieldLabel Group
33860     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33861     */
33862     register : function(label)
33863     {
33864         if(this.groups.hasOwnProperty(label.target)){
33865             return;
33866         }
33867      
33868         this.groups[label.target] = label;
33869         
33870     },
33871     /**
33872     * fetch a FieldLabel Group based on the target
33873     * @param {string} target
33874     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33875     */
33876     get: function(target) {
33877         if (typeof(this.groups[target]) == 'undefined') {
33878             return false;
33879         }
33880         
33881         return this.groups[target] ;
33882     }
33883 });
33884
33885  
33886
33887  /*
33888  * - LGPL
33889  *
33890  * page DateSplitField.
33891  * 
33892  */
33893
33894
33895 /**
33896  * @class Roo.bootstrap.DateSplitField
33897  * @extends Roo.bootstrap.Component
33898  * Bootstrap DateSplitField class
33899  * @cfg {string} fieldLabel - the label associated
33900  * @cfg {Number} labelWidth set the width of label (0-12)
33901  * @cfg {String} labelAlign (top|left)
33902  * @cfg {Boolean} dayAllowBlank (true|false) default false
33903  * @cfg {Boolean} monthAllowBlank (true|false) default false
33904  * @cfg {Boolean} yearAllowBlank (true|false) default false
33905  * @cfg {string} dayPlaceholder 
33906  * @cfg {string} monthPlaceholder
33907  * @cfg {string} yearPlaceholder
33908  * @cfg {string} dayFormat default 'd'
33909  * @cfg {string} monthFormat default 'm'
33910  * @cfg {string} yearFormat default 'Y'
33911  * @cfg {Number} labellg set the width of label (1-12)
33912  * @cfg {Number} labelmd set the width of label (1-12)
33913  * @cfg {Number} labelsm set the width of label (1-12)
33914  * @cfg {Number} labelxs set the width of label (1-12)
33915
33916  *     
33917  * @constructor
33918  * Create a new DateSplitField
33919  * @param {Object} config The config object
33920  */
33921
33922 Roo.bootstrap.DateSplitField = function(config){
33923     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33924     
33925     this.addEvents({
33926         // raw events
33927          /**
33928          * @event years
33929          * getting the data of years
33930          * @param {Roo.bootstrap.DateSplitField} this
33931          * @param {Object} years
33932          */
33933         "years" : true,
33934         /**
33935          * @event days
33936          * getting the data of days
33937          * @param {Roo.bootstrap.DateSplitField} this
33938          * @param {Object} days
33939          */
33940         "days" : true,
33941         /**
33942          * @event invalid
33943          * Fires after the field has been marked as invalid.
33944          * @param {Roo.form.Field} this
33945          * @param {String} msg The validation message
33946          */
33947         invalid : true,
33948        /**
33949          * @event valid
33950          * Fires after the field has been validated with no errors.
33951          * @param {Roo.form.Field} this
33952          */
33953         valid : true
33954     });
33955 };
33956
33957 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33958     
33959     fieldLabel : '',
33960     labelAlign : 'top',
33961     labelWidth : 3,
33962     dayAllowBlank : false,
33963     monthAllowBlank : false,
33964     yearAllowBlank : false,
33965     dayPlaceholder : '',
33966     monthPlaceholder : '',
33967     yearPlaceholder : '',
33968     dayFormat : 'd',
33969     monthFormat : 'm',
33970     yearFormat : 'Y',
33971     isFormField : true,
33972     labellg : 0,
33973     labelmd : 0,
33974     labelsm : 0,
33975     labelxs : 0,
33976     
33977     getAutoCreate : function()
33978     {
33979         var cfg = {
33980             tag : 'div',
33981             cls : 'row roo-date-split-field-group',
33982             cn : [
33983                 {
33984                     tag : 'input',
33985                     type : 'hidden',
33986                     cls : 'form-hidden-field roo-date-split-field-group-value',
33987                     name : this.name
33988                 }
33989             ]
33990         };
33991         
33992         var labelCls = 'col-md-12';
33993         var contentCls = 'col-md-4';
33994         
33995         if(this.fieldLabel){
33996             
33997             var label = {
33998                 tag : 'div',
33999                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34000                 cn : [
34001                     {
34002                         tag : 'label',
34003                         html : this.fieldLabel
34004                     }
34005                 ]
34006             };
34007             
34008             if(this.labelAlign == 'left'){
34009             
34010                 if(this.labelWidth > 12){
34011                     label.style = "width: " + this.labelWidth + 'px';
34012                 }
34013
34014                 if(this.labelWidth < 13 && this.labelmd == 0){
34015                     this.labelmd = this.labelWidth;
34016                 }
34017
34018                 if(this.labellg > 0){
34019                     labelCls = ' col-lg-' + this.labellg;
34020                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34021                 }
34022
34023                 if(this.labelmd > 0){
34024                     labelCls = ' col-md-' + this.labelmd;
34025                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34026                 }
34027
34028                 if(this.labelsm > 0){
34029                     labelCls = ' col-sm-' + this.labelsm;
34030                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34031                 }
34032
34033                 if(this.labelxs > 0){
34034                     labelCls = ' col-xs-' + this.labelxs;
34035                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34036                 }
34037             }
34038             
34039             label.cls += ' ' + labelCls;
34040             
34041             cfg.cn.push(label);
34042         }
34043         
34044         Roo.each(['day', 'month', 'year'], function(t){
34045             cfg.cn.push({
34046                 tag : 'div',
34047                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34048             });
34049         }, this);
34050         
34051         return cfg;
34052     },
34053     
34054     inputEl: function ()
34055     {
34056         return this.el.select('.roo-date-split-field-group-value', true).first();
34057     },
34058     
34059     onRender : function(ct, position) 
34060     {
34061         var _this = this;
34062         
34063         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34064         
34065         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34066         
34067         this.dayField = new Roo.bootstrap.ComboBox({
34068             allowBlank : this.dayAllowBlank,
34069             alwaysQuery : true,
34070             displayField : 'value',
34071             editable : false,
34072             fieldLabel : '',
34073             forceSelection : true,
34074             mode : 'local',
34075             placeholder : this.dayPlaceholder,
34076             selectOnFocus : true,
34077             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34078             triggerAction : 'all',
34079             typeAhead : true,
34080             valueField : 'value',
34081             store : new Roo.data.SimpleStore({
34082                 data : (function() {    
34083                     var days = [];
34084                     _this.fireEvent('days', _this, days);
34085                     return days;
34086                 })(),
34087                 fields : [ 'value' ]
34088             }),
34089             listeners : {
34090                 select : function (_self, record, index)
34091                 {
34092                     _this.setValue(_this.getValue());
34093                 }
34094             }
34095         });
34096
34097         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34098         
34099         this.monthField = new Roo.bootstrap.MonthField({
34100             after : '<i class=\"fa fa-calendar\"></i>',
34101             allowBlank : this.monthAllowBlank,
34102             placeholder : this.monthPlaceholder,
34103             readOnly : true,
34104             listeners : {
34105                 render : function (_self)
34106                 {
34107                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34108                         e.preventDefault();
34109                         _self.focus();
34110                     });
34111                 },
34112                 select : function (_self, oldvalue, newvalue)
34113                 {
34114                     _this.setValue(_this.getValue());
34115                 }
34116             }
34117         });
34118         
34119         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34120         
34121         this.yearField = new Roo.bootstrap.ComboBox({
34122             allowBlank : this.yearAllowBlank,
34123             alwaysQuery : true,
34124             displayField : 'value',
34125             editable : false,
34126             fieldLabel : '',
34127             forceSelection : true,
34128             mode : 'local',
34129             placeholder : this.yearPlaceholder,
34130             selectOnFocus : true,
34131             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34132             triggerAction : 'all',
34133             typeAhead : true,
34134             valueField : 'value',
34135             store : new Roo.data.SimpleStore({
34136                 data : (function() {
34137                     var years = [];
34138                     _this.fireEvent('years', _this, years);
34139                     return years;
34140                 })(),
34141                 fields : [ 'value' ]
34142             }),
34143             listeners : {
34144                 select : function (_self, record, index)
34145                 {
34146                     _this.setValue(_this.getValue());
34147                 }
34148             }
34149         });
34150
34151         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34152     },
34153     
34154     setValue : function(v, format)
34155     {
34156         this.inputEl.dom.value = v;
34157         
34158         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34159         
34160         var d = Date.parseDate(v, f);
34161         
34162         if(!d){
34163             this.validate();
34164             return;
34165         }
34166         
34167         this.setDay(d.format(this.dayFormat));
34168         this.setMonth(d.format(this.monthFormat));
34169         this.setYear(d.format(this.yearFormat));
34170         
34171         this.validate();
34172         
34173         return;
34174     },
34175     
34176     setDay : function(v)
34177     {
34178         this.dayField.setValue(v);
34179         this.inputEl.dom.value = this.getValue();
34180         this.validate();
34181         return;
34182     },
34183     
34184     setMonth : function(v)
34185     {
34186         this.monthField.setValue(v, true);
34187         this.inputEl.dom.value = this.getValue();
34188         this.validate();
34189         return;
34190     },
34191     
34192     setYear : function(v)
34193     {
34194         this.yearField.setValue(v);
34195         this.inputEl.dom.value = this.getValue();
34196         this.validate();
34197         return;
34198     },
34199     
34200     getDay : function()
34201     {
34202         return this.dayField.getValue();
34203     },
34204     
34205     getMonth : function()
34206     {
34207         return this.monthField.getValue();
34208     },
34209     
34210     getYear : function()
34211     {
34212         return this.yearField.getValue();
34213     },
34214     
34215     getValue : function()
34216     {
34217         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34218         
34219         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34220         
34221         return date;
34222     },
34223     
34224     reset : function()
34225     {
34226         this.setDay('');
34227         this.setMonth('');
34228         this.setYear('');
34229         this.inputEl.dom.value = '';
34230         this.validate();
34231         return;
34232     },
34233     
34234     validate : function()
34235     {
34236         var d = this.dayField.validate();
34237         var m = this.monthField.validate();
34238         var y = this.yearField.validate();
34239         
34240         var valid = true;
34241         
34242         if(
34243                 (!this.dayAllowBlank && !d) ||
34244                 (!this.monthAllowBlank && !m) ||
34245                 (!this.yearAllowBlank && !y)
34246         ){
34247             valid = false;
34248         }
34249         
34250         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34251             return valid;
34252         }
34253         
34254         if(valid){
34255             this.markValid();
34256             return valid;
34257         }
34258         
34259         this.markInvalid();
34260         
34261         return valid;
34262     },
34263     
34264     markValid : function()
34265     {
34266         
34267         var label = this.el.select('label', true).first();
34268         var icon = this.el.select('i.fa-star', true).first();
34269
34270         if(label && icon){
34271             icon.remove();
34272         }
34273         
34274         this.fireEvent('valid', this);
34275     },
34276     
34277      /**
34278      * Mark this field as invalid
34279      * @param {String} msg The validation message
34280      */
34281     markInvalid : function(msg)
34282     {
34283         
34284         var label = this.el.select('label', true).first();
34285         var icon = this.el.select('i.fa-star', true).first();
34286
34287         if(label && !icon){
34288             this.el.select('.roo-date-split-field-label', true).createChild({
34289                 tag : 'i',
34290                 cls : 'text-danger fa fa-lg fa-star',
34291                 tooltip : 'This field is required',
34292                 style : 'margin-right:5px;'
34293             }, label, true);
34294         }
34295         
34296         this.fireEvent('invalid', this, msg);
34297     },
34298     
34299     clearInvalid : function()
34300     {
34301         var label = this.el.select('label', true).first();
34302         var icon = this.el.select('i.fa-star', true).first();
34303
34304         if(label && icon){
34305             icon.remove();
34306         }
34307         
34308         this.fireEvent('valid', this);
34309     },
34310     
34311     getName: function()
34312     {
34313         return this.name;
34314     }
34315     
34316 });
34317
34318  /**
34319  *
34320  * This is based on 
34321  * http://masonry.desandro.com
34322  *
34323  * The idea is to render all the bricks based on vertical width...
34324  *
34325  * The original code extends 'outlayer' - we might need to use that....
34326  * 
34327  */
34328
34329
34330 /**
34331  * @class Roo.bootstrap.LayoutMasonry
34332  * @extends Roo.bootstrap.Component
34333  * Bootstrap Layout Masonry class
34334  * 
34335  * @constructor
34336  * Create a new Element
34337  * @param {Object} config The config object
34338  */
34339
34340 Roo.bootstrap.LayoutMasonry = function(config){
34341     
34342     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34343     
34344     this.bricks = [];
34345     
34346     Roo.bootstrap.LayoutMasonry.register(this);
34347     
34348     this.addEvents({
34349         // raw events
34350         /**
34351          * @event layout
34352          * Fire after layout the items
34353          * @param {Roo.bootstrap.LayoutMasonry} this
34354          * @param {Roo.EventObject} e
34355          */
34356         "layout" : true
34357     });
34358     
34359 };
34360
34361 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34362     
34363     /**
34364      * @cfg {Boolean} isLayoutInstant = no animation?
34365      */   
34366     isLayoutInstant : false, // needed?
34367    
34368     /**
34369      * @cfg {Number} boxWidth  width of the columns
34370      */   
34371     boxWidth : 450,
34372     
34373       /**
34374      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34375      */   
34376     boxHeight : 0,
34377     
34378     /**
34379      * @cfg {Number} padWidth padding below box..
34380      */   
34381     padWidth : 10, 
34382     
34383     /**
34384      * @cfg {Number} gutter gutter width..
34385      */   
34386     gutter : 10,
34387     
34388      /**
34389      * @cfg {Number} maxCols maximum number of columns
34390      */   
34391     
34392     maxCols: 0,
34393     
34394     /**
34395      * @cfg {Boolean} isAutoInitial defalut true
34396      */   
34397     isAutoInitial : true, 
34398     
34399     containerWidth: 0,
34400     
34401     /**
34402      * @cfg {Boolean} isHorizontal defalut false
34403      */   
34404     isHorizontal : false, 
34405
34406     currentSize : null,
34407     
34408     tag: 'div',
34409     
34410     cls: '',
34411     
34412     bricks: null, //CompositeElement
34413     
34414     cols : 1,
34415     
34416     _isLayoutInited : false,
34417     
34418 //    isAlternative : false, // only use for vertical layout...
34419     
34420     /**
34421      * @cfg {Number} alternativePadWidth padding below box..
34422      */   
34423     alternativePadWidth : 50,
34424     
34425     selectedBrick : [],
34426     
34427     getAutoCreate : function(){
34428         
34429         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34430         
34431         var cfg = {
34432             tag: this.tag,
34433             cls: 'blog-masonary-wrapper ' + this.cls,
34434             cn : {
34435                 cls : 'mas-boxes masonary'
34436             }
34437         };
34438         
34439         return cfg;
34440     },
34441     
34442     getChildContainer: function( )
34443     {
34444         if (this.boxesEl) {
34445             return this.boxesEl;
34446         }
34447         
34448         this.boxesEl = this.el.select('.mas-boxes').first();
34449         
34450         return this.boxesEl;
34451     },
34452     
34453     
34454     initEvents : function()
34455     {
34456         var _this = this;
34457         
34458         if(this.isAutoInitial){
34459             Roo.log('hook children rendered');
34460             this.on('childrenrendered', function() {
34461                 Roo.log('children rendered');
34462                 _this.initial();
34463             } ,this);
34464         }
34465     },
34466     
34467     initial : function()
34468     {
34469         this.selectedBrick = [];
34470         
34471         this.currentSize = this.el.getBox(true);
34472         
34473         Roo.EventManager.onWindowResize(this.resize, this); 
34474
34475         if(!this.isAutoInitial){
34476             this.layout();
34477             return;
34478         }
34479         
34480         this.layout();
34481         
34482         return;
34483         //this.layout.defer(500,this);
34484         
34485     },
34486     
34487     resize : function()
34488     {
34489         var cs = this.el.getBox(true);
34490         
34491         if (
34492                 this.currentSize.width == cs.width && 
34493                 this.currentSize.x == cs.x && 
34494                 this.currentSize.height == cs.height && 
34495                 this.currentSize.y == cs.y 
34496         ) {
34497             Roo.log("no change in with or X or Y");
34498             return;
34499         }
34500         
34501         this.currentSize = cs;
34502         
34503         this.layout();
34504         
34505     },
34506     
34507     layout : function()
34508     {   
34509         this._resetLayout();
34510         
34511         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34512         
34513         this.layoutItems( isInstant );
34514       
34515         this._isLayoutInited = true;
34516         
34517         this.fireEvent('layout', this);
34518         
34519     },
34520     
34521     _resetLayout : function()
34522     {
34523         if(this.isHorizontal){
34524             this.horizontalMeasureColumns();
34525             return;
34526         }
34527         
34528         this.verticalMeasureColumns();
34529         
34530     },
34531     
34532     verticalMeasureColumns : function()
34533     {
34534         this.getContainerWidth();
34535         
34536 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34537 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34538 //            return;
34539 //        }
34540         
34541         var boxWidth = this.boxWidth + this.padWidth;
34542         
34543         if(this.containerWidth < this.boxWidth){
34544             boxWidth = this.containerWidth
34545         }
34546         
34547         var containerWidth = this.containerWidth;
34548         
34549         var cols = Math.floor(containerWidth / boxWidth);
34550         
34551         this.cols = Math.max( cols, 1 );
34552         
34553         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34554         
34555         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34556         
34557         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34558         
34559         this.colWidth = boxWidth + avail - this.padWidth;
34560         
34561         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34562         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34563     },
34564     
34565     horizontalMeasureColumns : function()
34566     {
34567         this.getContainerWidth();
34568         
34569         var boxWidth = this.boxWidth;
34570         
34571         if(this.containerWidth < boxWidth){
34572             boxWidth = this.containerWidth;
34573         }
34574         
34575         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34576         
34577         this.el.setHeight(boxWidth);
34578         
34579     },
34580     
34581     getContainerWidth : function()
34582     {
34583         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34584     },
34585     
34586     layoutItems : function( isInstant )
34587     {
34588         Roo.log(this.bricks);
34589         
34590         var items = Roo.apply([], this.bricks);
34591         
34592         if(this.isHorizontal){
34593             this._horizontalLayoutItems( items , isInstant );
34594             return;
34595         }
34596         
34597 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34598 //            this._verticalAlternativeLayoutItems( items , isInstant );
34599 //            return;
34600 //        }
34601         
34602         this._verticalLayoutItems( items , isInstant );
34603         
34604     },
34605     
34606     _verticalLayoutItems : function ( items , isInstant)
34607     {
34608         if ( !items || !items.length ) {
34609             return;
34610         }
34611         
34612         var standard = [
34613             ['xs', 'xs', 'xs', 'tall'],
34614             ['xs', 'xs', 'tall'],
34615             ['xs', 'xs', 'sm'],
34616             ['xs', 'xs', 'xs'],
34617             ['xs', 'tall'],
34618             ['xs', 'sm'],
34619             ['xs', 'xs'],
34620             ['xs'],
34621             
34622             ['sm', 'xs', 'xs'],
34623             ['sm', 'xs'],
34624             ['sm'],
34625             
34626             ['tall', 'xs', 'xs', 'xs'],
34627             ['tall', 'xs', 'xs'],
34628             ['tall', 'xs'],
34629             ['tall']
34630             
34631         ];
34632         
34633         var queue = [];
34634         
34635         var boxes = [];
34636         
34637         var box = [];
34638         
34639         Roo.each(items, function(item, k){
34640             
34641             switch (item.size) {
34642                 // these layouts take up a full box,
34643                 case 'md' :
34644                 case 'md-left' :
34645                 case 'md-right' :
34646                 case 'wide' :
34647                     
34648                     if(box.length){
34649                         boxes.push(box);
34650                         box = [];
34651                     }
34652                     
34653                     boxes.push([item]);
34654                     
34655                     break;
34656                     
34657                 case 'xs' :
34658                 case 'sm' :
34659                 case 'tall' :
34660                     
34661                     box.push(item);
34662                     
34663                     break;
34664                 default :
34665                     break;
34666                     
34667             }
34668             
34669         }, this);
34670         
34671         if(box.length){
34672             boxes.push(box);
34673             box = [];
34674         }
34675         
34676         var filterPattern = function(box, length)
34677         {
34678             if(!box.length){
34679                 return;
34680             }
34681             
34682             var match = false;
34683             
34684             var pattern = box.slice(0, length);
34685             
34686             var format = [];
34687             
34688             Roo.each(pattern, function(i){
34689                 format.push(i.size);
34690             }, this);
34691             
34692             Roo.each(standard, function(s){
34693                 
34694                 if(String(s) != String(format)){
34695                     return;
34696                 }
34697                 
34698                 match = true;
34699                 return false;
34700                 
34701             }, this);
34702             
34703             if(!match && length == 1){
34704                 return;
34705             }
34706             
34707             if(!match){
34708                 filterPattern(box, length - 1);
34709                 return;
34710             }
34711                 
34712             queue.push(pattern);
34713
34714             box = box.slice(length, box.length);
34715
34716             filterPattern(box, 4);
34717
34718             return;
34719             
34720         }
34721         
34722         Roo.each(boxes, function(box, k){
34723             
34724             if(!box.length){
34725                 return;
34726             }
34727             
34728             if(box.length == 1){
34729                 queue.push(box);
34730                 return;
34731             }
34732             
34733             filterPattern(box, 4);
34734             
34735         }, this);
34736         
34737         this._processVerticalLayoutQueue( queue, isInstant );
34738         
34739     },
34740     
34741 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34742 //    {
34743 //        if ( !items || !items.length ) {
34744 //            return;
34745 //        }
34746 //
34747 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34748 //        
34749 //    },
34750     
34751     _horizontalLayoutItems : function ( items , isInstant)
34752     {
34753         if ( !items || !items.length || items.length < 3) {
34754             return;
34755         }
34756         
34757         items.reverse();
34758         
34759         var eItems = items.slice(0, 3);
34760         
34761         items = items.slice(3, items.length);
34762         
34763         var standard = [
34764             ['xs', 'xs', 'xs', 'wide'],
34765             ['xs', 'xs', 'wide'],
34766             ['xs', 'xs', 'sm'],
34767             ['xs', 'xs', 'xs'],
34768             ['xs', 'wide'],
34769             ['xs', 'sm'],
34770             ['xs', 'xs'],
34771             ['xs'],
34772             
34773             ['sm', 'xs', 'xs'],
34774             ['sm', 'xs'],
34775             ['sm'],
34776             
34777             ['wide', 'xs', 'xs', 'xs'],
34778             ['wide', 'xs', 'xs'],
34779             ['wide', 'xs'],
34780             ['wide'],
34781             
34782             ['wide-thin']
34783         ];
34784         
34785         var queue = [];
34786         
34787         var boxes = [];
34788         
34789         var box = [];
34790         
34791         Roo.each(items, function(item, k){
34792             
34793             switch (item.size) {
34794                 case 'md' :
34795                 case 'md-left' :
34796                 case 'md-right' :
34797                 case 'tall' :
34798                     
34799                     if(box.length){
34800                         boxes.push(box);
34801                         box = [];
34802                     }
34803                     
34804                     boxes.push([item]);
34805                     
34806                     break;
34807                     
34808                 case 'xs' :
34809                 case 'sm' :
34810                 case 'wide' :
34811                 case 'wide-thin' :
34812                     
34813                     box.push(item);
34814                     
34815                     break;
34816                 default :
34817                     break;
34818                     
34819             }
34820             
34821         }, this);
34822         
34823         if(box.length){
34824             boxes.push(box);
34825             box = [];
34826         }
34827         
34828         var filterPattern = function(box, length)
34829         {
34830             if(!box.length){
34831                 return;
34832             }
34833             
34834             var match = false;
34835             
34836             var pattern = box.slice(0, length);
34837             
34838             var format = [];
34839             
34840             Roo.each(pattern, function(i){
34841                 format.push(i.size);
34842             }, this);
34843             
34844             Roo.each(standard, function(s){
34845                 
34846                 if(String(s) != String(format)){
34847                     return;
34848                 }
34849                 
34850                 match = true;
34851                 return false;
34852                 
34853             }, this);
34854             
34855             if(!match && length == 1){
34856                 return;
34857             }
34858             
34859             if(!match){
34860                 filterPattern(box, length - 1);
34861                 return;
34862             }
34863                 
34864             queue.push(pattern);
34865
34866             box = box.slice(length, box.length);
34867
34868             filterPattern(box, 4);
34869
34870             return;
34871             
34872         }
34873         
34874         Roo.each(boxes, function(box, k){
34875             
34876             if(!box.length){
34877                 return;
34878             }
34879             
34880             if(box.length == 1){
34881                 queue.push(box);
34882                 return;
34883             }
34884             
34885             filterPattern(box, 4);
34886             
34887         }, this);
34888         
34889         
34890         var prune = [];
34891         
34892         var pos = this.el.getBox(true);
34893         
34894         var minX = pos.x;
34895         
34896         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34897         
34898         var hit_end = false;
34899         
34900         Roo.each(queue, function(box){
34901             
34902             if(hit_end){
34903                 
34904                 Roo.each(box, function(b){
34905                 
34906                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34907                     b.el.hide();
34908
34909                 }, this);
34910
34911                 return;
34912             }
34913             
34914             var mx = 0;
34915             
34916             Roo.each(box, function(b){
34917                 
34918                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34919                 b.el.show();
34920
34921                 mx = Math.max(mx, b.x);
34922                 
34923             }, this);
34924             
34925             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34926             
34927             if(maxX < minX){
34928                 
34929                 Roo.each(box, function(b){
34930                 
34931                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34932                     b.el.hide();
34933                     
34934                 }, this);
34935                 
34936                 hit_end = true;
34937                 
34938                 return;
34939             }
34940             
34941             prune.push(box);
34942             
34943         }, this);
34944         
34945         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34946     },
34947     
34948     /** Sets position of item in DOM
34949     * @param {Element} item
34950     * @param {Number} x - horizontal position
34951     * @param {Number} y - vertical position
34952     * @param {Boolean} isInstant - disables transitions
34953     */
34954     _processVerticalLayoutQueue : function( queue, isInstant )
34955     {
34956         var pos = this.el.getBox(true);
34957         var x = pos.x;
34958         var y = pos.y;
34959         var maxY = [];
34960         
34961         for (var i = 0; i < this.cols; i++){
34962             maxY[i] = pos.y;
34963         }
34964         
34965         Roo.each(queue, function(box, k){
34966             
34967             var col = k % this.cols;
34968             
34969             Roo.each(box, function(b,kk){
34970                 
34971                 b.el.position('absolute');
34972                 
34973                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34974                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34975                 
34976                 if(b.size == 'md-left' || b.size == 'md-right'){
34977                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34978                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34979                 }
34980                 
34981                 b.el.setWidth(width);
34982                 b.el.setHeight(height);
34983                 // iframe?
34984                 b.el.select('iframe',true).setSize(width,height);
34985                 
34986             }, this);
34987             
34988             for (var i = 0; i < this.cols; i++){
34989                 
34990                 if(maxY[i] < maxY[col]){
34991                     col = i;
34992                     continue;
34993                 }
34994                 
34995                 col = Math.min(col, i);
34996                 
34997             }
34998             
34999             x = pos.x + col * (this.colWidth + this.padWidth);
35000             
35001             y = maxY[col];
35002             
35003             var positions = [];
35004             
35005             switch (box.length){
35006                 case 1 :
35007                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35008                     break;
35009                 case 2 :
35010                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35011                     break;
35012                 case 3 :
35013                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35014                     break;
35015                 case 4 :
35016                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35017                     break;
35018                 default :
35019                     break;
35020             }
35021             
35022             Roo.each(box, function(b,kk){
35023                 
35024                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35025                 
35026                 var sz = b.el.getSize();
35027                 
35028                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35029                 
35030             }, this);
35031             
35032         }, this);
35033         
35034         var mY = 0;
35035         
35036         for (var i = 0; i < this.cols; i++){
35037             mY = Math.max(mY, maxY[i]);
35038         }
35039         
35040         this.el.setHeight(mY - pos.y);
35041         
35042     },
35043     
35044 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35045 //    {
35046 //        var pos = this.el.getBox(true);
35047 //        var x = pos.x;
35048 //        var y = pos.y;
35049 //        var maxX = pos.right;
35050 //        
35051 //        var maxHeight = 0;
35052 //        
35053 //        Roo.each(items, function(item, k){
35054 //            
35055 //            var c = k % 2;
35056 //            
35057 //            item.el.position('absolute');
35058 //                
35059 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35060 //
35061 //            item.el.setWidth(width);
35062 //
35063 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35064 //
35065 //            item.el.setHeight(height);
35066 //            
35067 //            if(c == 0){
35068 //                item.el.setXY([x, y], isInstant ? false : true);
35069 //            } else {
35070 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35071 //            }
35072 //            
35073 //            y = y + height + this.alternativePadWidth;
35074 //            
35075 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35076 //            
35077 //        }, this);
35078 //        
35079 //        this.el.setHeight(maxHeight);
35080 //        
35081 //    },
35082     
35083     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35084     {
35085         var pos = this.el.getBox(true);
35086         
35087         var minX = pos.x;
35088         var minY = pos.y;
35089         
35090         var maxX = pos.right;
35091         
35092         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35093         
35094         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35095         
35096         Roo.each(queue, function(box, k){
35097             
35098             Roo.each(box, function(b, kk){
35099                 
35100                 b.el.position('absolute');
35101                 
35102                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35103                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35104                 
35105                 if(b.size == 'md-left' || b.size == 'md-right'){
35106                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35107                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35108                 }
35109                 
35110                 b.el.setWidth(width);
35111                 b.el.setHeight(height);
35112                 
35113             }, this);
35114             
35115             if(!box.length){
35116                 return;
35117             }
35118             
35119             var positions = [];
35120             
35121             switch (box.length){
35122                 case 1 :
35123                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35124                     break;
35125                 case 2 :
35126                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35127                     break;
35128                 case 3 :
35129                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35130                     break;
35131                 case 4 :
35132                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35133                     break;
35134                 default :
35135                     break;
35136             }
35137             
35138             Roo.each(box, function(b,kk){
35139                 
35140                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35141                 
35142                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35143                 
35144             }, this);
35145             
35146         }, this);
35147         
35148     },
35149     
35150     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35151     {
35152         Roo.each(eItems, function(b,k){
35153             
35154             b.size = (k == 0) ? 'sm' : 'xs';
35155             b.x = (k == 0) ? 2 : 1;
35156             b.y = (k == 0) ? 2 : 1;
35157             
35158             b.el.position('absolute');
35159             
35160             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35161                 
35162             b.el.setWidth(width);
35163             
35164             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35165             
35166             b.el.setHeight(height);
35167             
35168         }, this);
35169
35170         var positions = [];
35171         
35172         positions.push({
35173             x : maxX - this.unitWidth * 2 - this.gutter,
35174             y : minY
35175         });
35176         
35177         positions.push({
35178             x : maxX - this.unitWidth,
35179             y : minY + (this.unitWidth + this.gutter) * 2
35180         });
35181         
35182         positions.push({
35183             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35184             y : minY
35185         });
35186         
35187         Roo.each(eItems, function(b,k){
35188             
35189             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35190
35191         }, this);
35192         
35193     },
35194     
35195     getVerticalOneBoxColPositions : function(x, y, box)
35196     {
35197         var pos = [];
35198         
35199         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35200         
35201         if(box[0].size == 'md-left'){
35202             rand = 0;
35203         }
35204         
35205         if(box[0].size == 'md-right'){
35206             rand = 1;
35207         }
35208         
35209         pos.push({
35210             x : x + (this.unitWidth + this.gutter) * rand,
35211             y : y
35212         });
35213         
35214         return pos;
35215     },
35216     
35217     getVerticalTwoBoxColPositions : function(x, y, box)
35218     {
35219         var pos = [];
35220         
35221         if(box[0].size == 'xs'){
35222             
35223             pos.push({
35224                 x : x,
35225                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35226             });
35227
35228             pos.push({
35229                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35230                 y : y
35231             });
35232             
35233             return pos;
35234             
35235         }
35236         
35237         pos.push({
35238             x : x,
35239             y : y
35240         });
35241
35242         pos.push({
35243             x : x + (this.unitWidth + this.gutter) * 2,
35244             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35245         });
35246         
35247         return pos;
35248         
35249     },
35250     
35251     getVerticalThreeBoxColPositions : function(x, y, box)
35252     {
35253         var pos = [];
35254         
35255         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35256             
35257             pos.push({
35258                 x : x,
35259                 y : y
35260             });
35261
35262             pos.push({
35263                 x : x + (this.unitWidth + this.gutter) * 1,
35264                 y : y
35265             });
35266             
35267             pos.push({
35268                 x : x + (this.unitWidth + this.gutter) * 2,
35269                 y : y
35270             });
35271             
35272             return pos;
35273             
35274         }
35275         
35276         if(box[0].size == 'xs' && box[1].size == 'xs'){
35277             
35278             pos.push({
35279                 x : x,
35280                 y : y
35281             });
35282
35283             pos.push({
35284                 x : x,
35285                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35286             });
35287             
35288             pos.push({
35289                 x : x + (this.unitWidth + this.gutter) * 1,
35290                 y : y
35291             });
35292             
35293             return pos;
35294             
35295         }
35296         
35297         pos.push({
35298             x : x,
35299             y : y
35300         });
35301
35302         pos.push({
35303             x : x + (this.unitWidth + this.gutter) * 2,
35304             y : y
35305         });
35306
35307         pos.push({
35308             x : x + (this.unitWidth + this.gutter) * 2,
35309             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35310         });
35311             
35312         return pos;
35313         
35314     },
35315     
35316     getVerticalFourBoxColPositions : function(x, y, box)
35317     {
35318         var pos = [];
35319         
35320         if(box[0].size == 'xs'){
35321             
35322             pos.push({
35323                 x : x,
35324                 y : y
35325             });
35326
35327             pos.push({
35328                 x : x,
35329                 y : y + (this.unitHeight + this.gutter) * 1
35330             });
35331             
35332             pos.push({
35333                 x : x,
35334                 y : y + (this.unitHeight + this.gutter) * 2
35335             });
35336             
35337             pos.push({
35338                 x : x + (this.unitWidth + this.gutter) * 1,
35339                 y : y
35340             });
35341             
35342             return pos;
35343             
35344         }
35345         
35346         pos.push({
35347             x : x,
35348             y : y
35349         });
35350
35351         pos.push({
35352             x : x + (this.unitWidth + this.gutter) * 2,
35353             y : y
35354         });
35355
35356         pos.push({
35357             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35358             y : y + (this.unitHeight + this.gutter) * 1
35359         });
35360
35361         pos.push({
35362             x : x + (this.unitWidth + this.gutter) * 2,
35363             y : y + (this.unitWidth + this.gutter) * 2
35364         });
35365
35366         return pos;
35367         
35368     },
35369     
35370     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35371     {
35372         var pos = [];
35373         
35374         if(box[0].size == 'md-left'){
35375             pos.push({
35376                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35377                 y : minY
35378             });
35379             
35380             return pos;
35381         }
35382         
35383         if(box[0].size == 'md-right'){
35384             pos.push({
35385                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35386                 y : minY + (this.unitWidth + this.gutter) * 1
35387             });
35388             
35389             return pos;
35390         }
35391         
35392         var rand = Math.floor(Math.random() * (4 - box[0].y));
35393         
35394         pos.push({
35395             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35396             y : minY + (this.unitWidth + this.gutter) * rand
35397         });
35398         
35399         return pos;
35400         
35401     },
35402     
35403     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35404     {
35405         var pos = [];
35406         
35407         if(box[0].size == 'xs'){
35408             
35409             pos.push({
35410                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35411                 y : minY
35412             });
35413
35414             pos.push({
35415                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35416                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35417             });
35418             
35419             return pos;
35420             
35421         }
35422         
35423         pos.push({
35424             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35425             y : minY
35426         });
35427
35428         pos.push({
35429             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35430             y : minY + (this.unitWidth + this.gutter) * 2
35431         });
35432         
35433         return pos;
35434         
35435     },
35436     
35437     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35438     {
35439         var pos = [];
35440         
35441         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35442             
35443             pos.push({
35444                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35445                 y : minY
35446             });
35447
35448             pos.push({
35449                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35450                 y : minY + (this.unitWidth + this.gutter) * 1
35451             });
35452             
35453             pos.push({
35454                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35455                 y : minY + (this.unitWidth + this.gutter) * 2
35456             });
35457             
35458             return pos;
35459             
35460         }
35461         
35462         if(box[0].size == 'xs' && box[1].size == 'xs'){
35463             
35464             pos.push({
35465                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35466                 y : minY
35467             });
35468
35469             pos.push({
35470                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35471                 y : minY
35472             });
35473             
35474             pos.push({
35475                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35476                 y : minY + (this.unitWidth + this.gutter) * 1
35477             });
35478             
35479             return pos;
35480             
35481         }
35482         
35483         pos.push({
35484             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35485             y : minY
35486         });
35487
35488         pos.push({
35489             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35490             y : minY + (this.unitWidth + this.gutter) * 2
35491         });
35492
35493         pos.push({
35494             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35495             y : minY + (this.unitWidth + this.gutter) * 2
35496         });
35497             
35498         return pos;
35499         
35500     },
35501     
35502     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35503     {
35504         var pos = [];
35505         
35506         if(box[0].size == 'xs'){
35507             
35508             pos.push({
35509                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35510                 y : minY
35511             });
35512
35513             pos.push({
35514                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35515                 y : minY
35516             });
35517             
35518             pos.push({
35519                 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),
35520                 y : minY
35521             });
35522             
35523             pos.push({
35524                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35525                 y : minY + (this.unitWidth + this.gutter) * 1
35526             });
35527             
35528             return pos;
35529             
35530         }
35531         
35532         pos.push({
35533             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35534             y : minY
35535         });
35536         
35537         pos.push({
35538             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35539             y : minY + (this.unitWidth + this.gutter) * 2
35540         });
35541         
35542         pos.push({
35543             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35544             y : minY + (this.unitWidth + this.gutter) * 2
35545         });
35546         
35547         pos.push({
35548             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),
35549             y : minY + (this.unitWidth + this.gutter) * 2
35550         });
35551
35552         return pos;
35553         
35554     },
35555     
35556     /**
35557     * remove a Masonry Brick
35558     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35559     */
35560     removeBrick : function(brick_id)
35561     {
35562         if (!brick_id) {
35563             return;
35564         }
35565         
35566         for (var i = 0; i<this.bricks.length; i++) {
35567             if (this.bricks[i].id == brick_id) {
35568                 this.bricks.splice(i,1);
35569                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35570                 this.initial();
35571             }
35572         }
35573     },
35574     
35575     /**
35576     * adds a Masonry Brick
35577     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35578     */
35579     addBrick : function(cfg)
35580     {
35581         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35582         //this.register(cn);
35583         cn.parentId = this.id;
35584         cn.render(this.el);
35585         return cn;
35586     },
35587     
35588     /**
35589     * register a Masonry Brick
35590     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35591     */
35592     
35593     register : function(brick)
35594     {
35595         this.bricks.push(brick);
35596         brick.masonryId = this.id;
35597     },
35598     
35599     /**
35600     * clear all the Masonry Brick
35601     */
35602     clearAll : function()
35603     {
35604         this.bricks = [];
35605         //this.getChildContainer().dom.innerHTML = "";
35606         this.el.dom.innerHTML = '';
35607     },
35608     
35609     getSelected : function()
35610     {
35611         if (!this.selectedBrick) {
35612             return false;
35613         }
35614         
35615         return this.selectedBrick;
35616     }
35617 });
35618
35619 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35620     
35621     groups: {},
35622      /**
35623     * register a Masonry Layout
35624     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35625     */
35626     
35627     register : function(layout)
35628     {
35629         this.groups[layout.id] = layout;
35630     },
35631     /**
35632     * fetch a  Masonry Layout based on the masonry layout ID
35633     * @param {string} the masonry layout to add
35634     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35635     */
35636     
35637     get: function(layout_id) {
35638         if (typeof(this.groups[layout_id]) == 'undefined') {
35639             return false;
35640         }
35641         return this.groups[layout_id] ;
35642     }
35643     
35644     
35645     
35646 });
35647
35648  
35649
35650  /**
35651  *
35652  * This is based on 
35653  * http://masonry.desandro.com
35654  *
35655  * The idea is to render all the bricks based on vertical width...
35656  *
35657  * The original code extends 'outlayer' - we might need to use that....
35658  * 
35659  */
35660
35661
35662 /**
35663  * @class Roo.bootstrap.LayoutMasonryAuto
35664  * @extends Roo.bootstrap.Component
35665  * Bootstrap Layout Masonry class
35666  * 
35667  * @constructor
35668  * Create a new Element
35669  * @param {Object} config The config object
35670  */
35671
35672 Roo.bootstrap.LayoutMasonryAuto = function(config){
35673     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35674 };
35675
35676 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35677     
35678       /**
35679      * @cfg {Boolean} isFitWidth  - resize the width..
35680      */   
35681     isFitWidth : false,  // options..
35682     /**
35683      * @cfg {Boolean} isOriginLeft = left align?
35684      */   
35685     isOriginLeft : true,
35686     /**
35687      * @cfg {Boolean} isOriginTop = top align?
35688      */   
35689     isOriginTop : false,
35690     /**
35691      * @cfg {Boolean} isLayoutInstant = no animation?
35692      */   
35693     isLayoutInstant : false, // needed?
35694     /**
35695      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35696      */   
35697     isResizingContainer : true,
35698     /**
35699      * @cfg {Number} columnWidth  width of the columns 
35700      */   
35701     
35702     columnWidth : 0,
35703     
35704     /**
35705      * @cfg {Number} maxCols maximum number of columns
35706      */   
35707     
35708     maxCols: 0,
35709     /**
35710      * @cfg {Number} padHeight padding below box..
35711      */   
35712     
35713     padHeight : 10, 
35714     
35715     /**
35716      * @cfg {Boolean} isAutoInitial defalut true
35717      */   
35718     
35719     isAutoInitial : true, 
35720     
35721     // private?
35722     gutter : 0,
35723     
35724     containerWidth: 0,
35725     initialColumnWidth : 0,
35726     currentSize : null,
35727     
35728     colYs : null, // array.
35729     maxY : 0,
35730     padWidth: 10,
35731     
35732     
35733     tag: 'div',
35734     cls: '',
35735     bricks: null, //CompositeElement
35736     cols : 0, // array?
35737     // element : null, // wrapped now this.el
35738     _isLayoutInited : null, 
35739     
35740     
35741     getAutoCreate : function(){
35742         
35743         var cfg = {
35744             tag: this.tag,
35745             cls: 'blog-masonary-wrapper ' + this.cls,
35746             cn : {
35747                 cls : 'mas-boxes masonary'
35748             }
35749         };
35750         
35751         return cfg;
35752     },
35753     
35754     getChildContainer: function( )
35755     {
35756         if (this.boxesEl) {
35757             return this.boxesEl;
35758         }
35759         
35760         this.boxesEl = this.el.select('.mas-boxes').first();
35761         
35762         return this.boxesEl;
35763     },
35764     
35765     
35766     initEvents : function()
35767     {
35768         var _this = this;
35769         
35770         if(this.isAutoInitial){
35771             Roo.log('hook children rendered');
35772             this.on('childrenrendered', function() {
35773                 Roo.log('children rendered');
35774                 _this.initial();
35775             } ,this);
35776         }
35777         
35778     },
35779     
35780     initial : function()
35781     {
35782         this.reloadItems();
35783
35784         this.currentSize = this.el.getBox(true);
35785
35786         /// was window resize... - let's see if this works..
35787         Roo.EventManager.onWindowResize(this.resize, this); 
35788
35789         if(!this.isAutoInitial){
35790             this.layout();
35791             return;
35792         }
35793         
35794         this.layout.defer(500,this);
35795     },
35796     
35797     reloadItems: function()
35798     {
35799         this.bricks = this.el.select('.masonry-brick', true);
35800         
35801         this.bricks.each(function(b) {
35802             //Roo.log(b.getSize());
35803             if (!b.attr('originalwidth')) {
35804                 b.attr('originalwidth',  b.getSize().width);
35805             }
35806             
35807         });
35808         
35809         Roo.log(this.bricks.elements.length);
35810     },
35811     
35812     resize : function()
35813     {
35814         Roo.log('resize');
35815         var cs = this.el.getBox(true);
35816         
35817         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35818             Roo.log("no change in with or X");
35819             return;
35820         }
35821         this.currentSize = cs;
35822         this.layout();
35823     },
35824     
35825     layout : function()
35826     {
35827          Roo.log('layout');
35828         this._resetLayout();
35829         //this._manageStamps();
35830       
35831         // don't animate first layout
35832         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35833         this.layoutItems( isInstant );
35834       
35835         // flag for initalized
35836         this._isLayoutInited = true;
35837     },
35838     
35839     layoutItems : function( isInstant )
35840     {
35841         //var items = this._getItemsForLayout( this.items );
35842         // original code supports filtering layout items.. we just ignore it..
35843         
35844         this._layoutItems( this.bricks , isInstant );
35845       
35846         this._postLayout();
35847     },
35848     _layoutItems : function ( items , isInstant)
35849     {
35850        //this.fireEvent( 'layout', this, items );
35851     
35852
35853         if ( !items || !items.elements.length ) {
35854           // no items, emit event with empty array
35855             return;
35856         }
35857
35858         var queue = [];
35859         items.each(function(item) {
35860             Roo.log("layout item");
35861             Roo.log(item);
35862             // get x/y object from method
35863             var position = this._getItemLayoutPosition( item );
35864             // enqueue
35865             position.item = item;
35866             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35867             queue.push( position );
35868         }, this);
35869       
35870         this._processLayoutQueue( queue );
35871     },
35872     /** Sets position of item in DOM
35873     * @param {Element} item
35874     * @param {Number} x - horizontal position
35875     * @param {Number} y - vertical position
35876     * @param {Boolean} isInstant - disables transitions
35877     */
35878     _processLayoutQueue : function( queue )
35879     {
35880         for ( var i=0, len = queue.length; i < len; i++ ) {
35881             var obj = queue[i];
35882             obj.item.position('absolute');
35883             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35884         }
35885     },
35886       
35887     
35888     /**
35889     * Any logic you want to do after each layout,
35890     * i.e. size the container
35891     */
35892     _postLayout : function()
35893     {
35894         this.resizeContainer();
35895     },
35896     
35897     resizeContainer : function()
35898     {
35899         if ( !this.isResizingContainer ) {
35900             return;
35901         }
35902         var size = this._getContainerSize();
35903         if ( size ) {
35904             this.el.setSize(size.width,size.height);
35905             this.boxesEl.setSize(size.width,size.height);
35906         }
35907     },
35908     
35909     
35910     
35911     _resetLayout : function()
35912     {
35913         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35914         this.colWidth = this.el.getWidth();
35915         //this.gutter = this.el.getWidth(); 
35916         
35917         this.measureColumns();
35918
35919         // reset column Y
35920         var i = this.cols;
35921         this.colYs = [];
35922         while (i--) {
35923             this.colYs.push( 0 );
35924         }
35925     
35926         this.maxY = 0;
35927     },
35928
35929     measureColumns : function()
35930     {
35931         this.getContainerWidth();
35932       // if columnWidth is 0, default to outerWidth of first item
35933         if ( !this.columnWidth ) {
35934             var firstItem = this.bricks.first();
35935             Roo.log(firstItem);
35936             this.columnWidth  = this.containerWidth;
35937             if (firstItem && firstItem.attr('originalwidth') ) {
35938                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35939             }
35940             // columnWidth fall back to item of first element
35941             Roo.log("set column width?");
35942                         this.initialColumnWidth = this.columnWidth  ;
35943
35944             // if first elem has no width, default to size of container
35945             
35946         }
35947         
35948         
35949         if (this.initialColumnWidth) {
35950             this.columnWidth = this.initialColumnWidth;
35951         }
35952         
35953         
35954             
35955         // column width is fixed at the top - however if container width get's smaller we should
35956         // reduce it...
35957         
35958         // this bit calcs how man columns..
35959             
35960         var columnWidth = this.columnWidth += this.gutter;
35961       
35962         // calculate columns
35963         var containerWidth = this.containerWidth + this.gutter;
35964         
35965         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35966         // fix rounding errors, typically with gutters
35967         var excess = columnWidth - containerWidth % columnWidth;
35968         
35969         
35970         // if overshoot is less than a pixel, round up, otherwise floor it
35971         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35972         cols = Math[ mathMethod ]( cols );
35973         this.cols = Math.max( cols, 1 );
35974         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35975         
35976          // padding positioning..
35977         var totalColWidth = this.cols * this.columnWidth;
35978         var padavail = this.containerWidth - totalColWidth;
35979         // so for 2 columns - we need 3 'pads'
35980         
35981         var padNeeded = (1+this.cols) * this.padWidth;
35982         
35983         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35984         
35985         this.columnWidth += padExtra
35986         //this.padWidth = Math.floor(padavail /  ( this.cols));
35987         
35988         // adjust colum width so that padding is fixed??
35989         
35990         // we have 3 columns ... total = width * 3
35991         // we have X left over... that should be used by 
35992         
35993         //if (this.expandC) {
35994             
35995         //}
35996         
35997         
35998         
35999     },
36000     
36001     getContainerWidth : function()
36002     {
36003        /* // container is parent if fit width
36004         var container = this.isFitWidth ? this.element.parentNode : this.element;
36005         // check that this.size and size are there
36006         // IE8 triggers resize on body size change, so they might not be
36007         
36008         var size = getSize( container );  //FIXME
36009         this.containerWidth = size && size.innerWidth; //FIXME
36010         */
36011          
36012         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36013         
36014     },
36015     
36016     _getItemLayoutPosition : function( item )  // what is item?
36017     {
36018         // we resize the item to our columnWidth..
36019       
36020         item.setWidth(this.columnWidth);
36021         item.autoBoxAdjust  = false;
36022         
36023         var sz = item.getSize();
36024  
36025         // how many columns does this brick span
36026         var remainder = this.containerWidth % this.columnWidth;
36027         
36028         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36029         // round if off by 1 pixel, otherwise use ceil
36030         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36031         colSpan = Math.min( colSpan, this.cols );
36032         
36033         // normally this should be '1' as we dont' currently allow multi width columns..
36034         
36035         var colGroup = this._getColGroup( colSpan );
36036         // get the minimum Y value from the columns
36037         var minimumY = Math.min.apply( Math, colGroup );
36038         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36039         
36040         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36041          
36042         // position the brick
36043         var position = {
36044             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36045             y: this.currentSize.y + minimumY + this.padHeight
36046         };
36047         
36048         Roo.log(position);
36049         // apply setHeight to necessary columns
36050         var setHeight = minimumY + sz.height + this.padHeight;
36051         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36052         
36053         var setSpan = this.cols + 1 - colGroup.length;
36054         for ( var i = 0; i < setSpan; i++ ) {
36055           this.colYs[ shortColIndex + i ] = setHeight ;
36056         }
36057       
36058         return position;
36059     },
36060     
36061     /**
36062      * @param {Number} colSpan - number of columns the element spans
36063      * @returns {Array} colGroup
36064      */
36065     _getColGroup : function( colSpan )
36066     {
36067         if ( colSpan < 2 ) {
36068           // if brick spans only one column, use all the column Ys
36069           return this.colYs;
36070         }
36071       
36072         var colGroup = [];
36073         // how many different places could this brick fit horizontally
36074         var groupCount = this.cols + 1 - colSpan;
36075         // for each group potential horizontal position
36076         for ( var i = 0; i < groupCount; i++ ) {
36077           // make an array of colY values for that one group
36078           var groupColYs = this.colYs.slice( i, i + colSpan );
36079           // and get the max value of the array
36080           colGroup[i] = Math.max.apply( Math, groupColYs );
36081         }
36082         return colGroup;
36083     },
36084     /*
36085     _manageStamp : function( stamp )
36086     {
36087         var stampSize =  stamp.getSize();
36088         var offset = stamp.getBox();
36089         // get the columns that this stamp affects
36090         var firstX = this.isOriginLeft ? offset.x : offset.right;
36091         var lastX = firstX + stampSize.width;
36092         var firstCol = Math.floor( firstX / this.columnWidth );
36093         firstCol = Math.max( 0, firstCol );
36094         
36095         var lastCol = Math.floor( lastX / this.columnWidth );
36096         // lastCol should not go over if multiple of columnWidth #425
36097         lastCol -= lastX % this.columnWidth ? 0 : 1;
36098         lastCol = Math.min( this.cols - 1, lastCol );
36099         
36100         // set colYs to bottom of the stamp
36101         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36102             stampSize.height;
36103             
36104         for ( var i = firstCol; i <= lastCol; i++ ) {
36105           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36106         }
36107     },
36108     */
36109     
36110     _getContainerSize : function()
36111     {
36112         this.maxY = Math.max.apply( Math, this.colYs );
36113         var size = {
36114             height: this.maxY
36115         };
36116       
36117         if ( this.isFitWidth ) {
36118             size.width = this._getContainerFitWidth();
36119         }
36120       
36121         return size;
36122     },
36123     
36124     _getContainerFitWidth : function()
36125     {
36126         var unusedCols = 0;
36127         // count unused columns
36128         var i = this.cols;
36129         while ( --i ) {
36130           if ( this.colYs[i] !== 0 ) {
36131             break;
36132           }
36133           unusedCols++;
36134         }
36135         // fit container to columns that have been used
36136         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36137     },
36138     
36139     needsResizeLayout : function()
36140     {
36141         var previousWidth = this.containerWidth;
36142         this.getContainerWidth();
36143         return previousWidth !== this.containerWidth;
36144     }
36145  
36146 });
36147
36148  
36149
36150  /*
36151  * - LGPL
36152  *
36153  * element
36154  * 
36155  */
36156
36157 /**
36158  * @class Roo.bootstrap.MasonryBrick
36159  * @extends Roo.bootstrap.Component
36160  * Bootstrap MasonryBrick class
36161  * 
36162  * @constructor
36163  * Create a new MasonryBrick
36164  * @param {Object} config The config object
36165  */
36166
36167 Roo.bootstrap.MasonryBrick = function(config){
36168     
36169     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36170     
36171     Roo.bootstrap.MasonryBrick.register(this);
36172     
36173     this.addEvents({
36174         // raw events
36175         /**
36176          * @event click
36177          * When a MasonryBrick is clcik
36178          * @param {Roo.bootstrap.MasonryBrick} this
36179          * @param {Roo.EventObject} e
36180          */
36181         "click" : true
36182     });
36183 };
36184
36185 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36186     
36187     /**
36188      * @cfg {String} title
36189      */   
36190     title : '',
36191     /**
36192      * @cfg {String} html
36193      */   
36194     html : '',
36195     /**
36196      * @cfg {String} bgimage
36197      */   
36198     bgimage : '',
36199     /**
36200      * @cfg {String} videourl
36201      */   
36202     videourl : '',
36203     /**
36204      * @cfg {String} cls
36205      */   
36206     cls : '',
36207     /**
36208      * @cfg {String} href
36209      */   
36210     href : '',
36211     /**
36212      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36213      */   
36214     size : 'xs',
36215     
36216     /**
36217      * @cfg {String} placetitle (center|bottom)
36218      */   
36219     placetitle : '',
36220     
36221     /**
36222      * @cfg {Boolean} isFitContainer defalut true
36223      */   
36224     isFitContainer : true, 
36225     
36226     /**
36227      * @cfg {Boolean} preventDefault defalut false
36228      */   
36229     preventDefault : false, 
36230     
36231     /**
36232      * @cfg {Boolean} inverse defalut false
36233      */   
36234     maskInverse : false, 
36235     
36236     getAutoCreate : function()
36237     {
36238         if(!this.isFitContainer){
36239             return this.getSplitAutoCreate();
36240         }
36241         
36242         var cls = 'masonry-brick masonry-brick-full';
36243         
36244         if(this.href.length){
36245             cls += ' masonry-brick-link';
36246         }
36247         
36248         if(this.bgimage.length){
36249             cls += ' masonry-brick-image';
36250         }
36251         
36252         if(this.maskInverse){
36253             cls += ' mask-inverse';
36254         }
36255         
36256         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36257             cls += ' enable-mask';
36258         }
36259         
36260         if(this.size){
36261             cls += ' masonry-' + this.size + '-brick';
36262         }
36263         
36264         if(this.placetitle.length){
36265             
36266             switch (this.placetitle) {
36267                 case 'center' :
36268                     cls += ' masonry-center-title';
36269                     break;
36270                 case 'bottom' :
36271                     cls += ' masonry-bottom-title';
36272                     break;
36273                 default:
36274                     break;
36275             }
36276             
36277         } else {
36278             if(!this.html.length && !this.bgimage.length){
36279                 cls += ' masonry-center-title';
36280             }
36281
36282             if(!this.html.length && this.bgimage.length){
36283                 cls += ' masonry-bottom-title';
36284             }
36285         }
36286         
36287         if(this.cls){
36288             cls += ' ' + this.cls;
36289         }
36290         
36291         var cfg = {
36292             tag: (this.href.length) ? 'a' : 'div',
36293             cls: cls,
36294             cn: [
36295                 {
36296                     tag: 'div',
36297                     cls: 'masonry-brick-mask'
36298                 },
36299                 {
36300                     tag: 'div',
36301                     cls: 'masonry-brick-paragraph',
36302                     cn: []
36303                 }
36304             ]
36305         };
36306         
36307         if(this.href.length){
36308             cfg.href = this.href;
36309         }
36310         
36311         var cn = cfg.cn[1].cn;
36312         
36313         if(this.title.length){
36314             cn.push({
36315                 tag: 'h4',
36316                 cls: 'masonry-brick-title',
36317                 html: this.title
36318             });
36319         }
36320         
36321         if(this.html.length){
36322             cn.push({
36323                 tag: 'p',
36324                 cls: 'masonry-brick-text',
36325                 html: this.html
36326             });
36327         }
36328         
36329         if (!this.title.length && !this.html.length) {
36330             cfg.cn[1].cls += ' hide';
36331         }
36332         
36333         if(this.bgimage.length){
36334             cfg.cn.push({
36335                 tag: 'img',
36336                 cls: 'masonry-brick-image-view',
36337                 src: this.bgimage
36338             });
36339         }
36340         
36341         if(this.videourl.length){
36342             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36343             // youtube support only?
36344             cfg.cn.push({
36345                 tag: 'iframe',
36346                 cls: 'masonry-brick-image-view',
36347                 src: vurl,
36348                 frameborder : 0,
36349                 allowfullscreen : true
36350             });
36351         }
36352         
36353         return cfg;
36354         
36355     },
36356     
36357     getSplitAutoCreate : function()
36358     {
36359         var cls = 'masonry-brick masonry-brick-split';
36360         
36361         if(this.href.length){
36362             cls += ' masonry-brick-link';
36363         }
36364         
36365         if(this.bgimage.length){
36366             cls += ' masonry-brick-image';
36367         }
36368         
36369         if(this.size){
36370             cls += ' masonry-' + this.size + '-brick';
36371         }
36372         
36373         switch (this.placetitle) {
36374             case 'center' :
36375                 cls += ' masonry-center-title';
36376                 break;
36377             case 'bottom' :
36378                 cls += ' masonry-bottom-title';
36379                 break;
36380             default:
36381                 if(!this.bgimage.length){
36382                     cls += ' masonry-center-title';
36383                 }
36384
36385                 if(this.bgimage.length){
36386                     cls += ' masonry-bottom-title';
36387                 }
36388                 break;
36389         }
36390         
36391         if(this.cls){
36392             cls += ' ' + this.cls;
36393         }
36394         
36395         var cfg = {
36396             tag: (this.href.length) ? 'a' : 'div',
36397             cls: cls,
36398             cn: [
36399                 {
36400                     tag: 'div',
36401                     cls: 'masonry-brick-split-head',
36402                     cn: [
36403                         {
36404                             tag: 'div',
36405                             cls: 'masonry-brick-paragraph',
36406                             cn: []
36407                         }
36408                     ]
36409                 },
36410                 {
36411                     tag: 'div',
36412                     cls: 'masonry-brick-split-body',
36413                     cn: []
36414                 }
36415             ]
36416         };
36417         
36418         if(this.href.length){
36419             cfg.href = this.href;
36420         }
36421         
36422         if(this.title.length){
36423             cfg.cn[0].cn[0].cn.push({
36424                 tag: 'h4',
36425                 cls: 'masonry-brick-title',
36426                 html: this.title
36427             });
36428         }
36429         
36430         if(this.html.length){
36431             cfg.cn[1].cn.push({
36432                 tag: 'p',
36433                 cls: 'masonry-brick-text',
36434                 html: this.html
36435             });
36436         }
36437
36438         if(this.bgimage.length){
36439             cfg.cn[0].cn.push({
36440                 tag: 'img',
36441                 cls: 'masonry-brick-image-view',
36442                 src: this.bgimage
36443             });
36444         }
36445         
36446         if(this.videourl.length){
36447             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36448             // youtube support only?
36449             cfg.cn[0].cn.cn.push({
36450                 tag: 'iframe',
36451                 cls: 'masonry-brick-image-view',
36452                 src: vurl,
36453                 frameborder : 0,
36454                 allowfullscreen : true
36455             });
36456         }
36457         
36458         return cfg;
36459     },
36460     
36461     initEvents: function() 
36462     {
36463         switch (this.size) {
36464             case 'xs' :
36465                 this.x = 1;
36466                 this.y = 1;
36467                 break;
36468             case 'sm' :
36469                 this.x = 2;
36470                 this.y = 2;
36471                 break;
36472             case 'md' :
36473             case 'md-left' :
36474             case 'md-right' :
36475                 this.x = 3;
36476                 this.y = 3;
36477                 break;
36478             case 'tall' :
36479                 this.x = 2;
36480                 this.y = 3;
36481                 break;
36482             case 'wide' :
36483                 this.x = 3;
36484                 this.y = 2;
36485                 break;
36486             case 'wide-thin' :
36487                 this.x = 3;
36488                 this.y = 1;
36489                 break;
36490                         
36491             default :
36492                 break;
36493         }
36494         
36495         if(Roo.isTouch){
36496             this.el.on('touchstart', this.onTouchStart, this);
36497             this.el.on('touchmove', this.onTouchMove, this);
36498             this.el.on('touchend', this.onTouchEnd, this);
36499             this.el.on('contextmenu', this.onContextMenu, this);
36500         } else {
36501             this.el.on('mouseenter'  ,this.enter, this);
36502             this.el.on('mouseleave', this.leave, this);
36503             this.el.on('click', this.onClick, this);
36504         }
36505         
36506         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36507             this.parent().bricks.push(this);   
36508         }
36509         
36510     },
36511     
36512     onClick: function(e, el)
36513     {
36514         var time = this.endTimer - this.startTimer;
36515         // Roo.log(e.preventDefault());
36516         if(Roo.isTouch){
36517             if(time > 1000){
36518                 e.preventDefault();
36519                 return;
36520             }
36521         }
36522         
36523         if(!this.preventDefault){
36524             return;
36525         }
36526         
36527         e.preventDefault();
36528         
36529         if (this.activeClass != '') {
36530             this.selectBrick();
36531         }
36532         
36533         this.fireEvent('click', this, e);
36534     },
36535     
36536     enter: function(e, el)
36537     {
36538         e.preventDefault();
36539         
36540         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36541             return;
36542         }
36543         
36544         if(this.bgimage.length && this.html.length){
36545             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36546         }
36547     },
36548     
36549     leave: function(e, el)
36550     {
36551         e.preventDefault();
36552         
36553         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36554             return;
36555         }
36556         
36557         if(this.bgimage.length && this.html.length){
36558             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36559         }
36560     },
36561     
36562     onTouchStart: function(e, el)
36563     {
36564 //        e.preventDefault();
36565         
36566         this.touchmoved = false;
36567         
36568         if(!this.isFitContainer){
36569             return;
36570         }
36571         
36572         if(!this.bgimage.length || !this.html.length){
36573             return;
36574         }
36575         
36576         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36577         
36578         this.timer = new Date().getTime();
36579         
36580     },
36581     
36582     onTouchMove: function(e, el)
36583     {
36584         this.touchmoved = true;
36585     },
36586     
36587     onContextMenu : function(e,el)
36588     {
36589         e.preventDefault();
36590         e.stopPropagation();
36591         return false;
36592     },
36593     
36594     onTouchEnd: function(e, el)
36595     {
36596 //        e.preventDefault();
36597         
36598         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36599         
36600             this.leave(e,el);
36601             
36602             return;
36603         }
36604         
36605         if(!this.bgimage.length || !this.html.length){
36606             
36607             if(this.href.length){
36608                 window.location.href = this.href;
36609             }
36610             
36611             return;
36612         }
36613         
36614         if(!this.isFitContainer){
36615             return;
36616         }
36617         
36618         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36619         
36620         window.location.href = this.href;
36621     },
36622     
36623     //selection on single brick only
36624     selectBrick : function() {
36625         
36626         if (!this.parentId) {
36627             return;
36628         }
36629         
36630         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36631         var index = m.selectedBrick.indexOf(this.id);
36632         
36633         if ( index > -1) {
36634             m.selectedBrick.splice(index,1);
36635             this.el.removeClass(this.activeClass);
36636             return;
36637         }
36638         
36639         for(var i = 0; i < m.selectedBrick.length; i++) {
36640             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36641             b.el.removeClass(b.activeClass);
36642         }
36643         
36644         m.selectedBrick = [];
36645         
36646         m.selectedBrick.push(this.id);
36647         this.el.addClass(this.activeClass);
36648         return;
36649     },
36650     
36651     isSelected : function(){
36652         return this.el.hasClass(this.activeClass);
36653         
36654     }
36655 });
36656
36657 Roo.apply(Roo.bootstrap.MasonryBrick, {
36658     
36659     //groups: {},
36660     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36661      /**
36662     * register a Masonry Brick
36663     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36664     */
36665     
36666     register : function(brick)
36667     {
36668         //this.groups[brick.id] = brick;
36669         this.groups.add(brick.id, brick);
36670     },
36671     /**
36672     * fetch a  masonry brick based on the masonry brick ID
36673     * @param {string} the masonry brick to add
36674     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36675     */
36676     
36677     get: function(brick_id) 
36678     {
36679         // if (typeof(this.groups[brick_id]) == 'undefined') {
36680         //     return false;
36681         // }
36682         // return this.groups[brick_id] ;
36683         
36684         if(this.groups.key(brick_id)) {
36685             return this.groups.key(brick_id);
36686         }
36687         
36688         return false;
36689     }
36690     
36691     
36692     
36693 });
36694
36695  /*
36696  * - LGPL
36697  *
36698  * element
36699  * 
36700  */
36701
36702 /**
36703  * @class Roo.bootstrap.Brick
36704  * @extends Roo.bootstrap.Component
36705  * Bootstrap Brick class
36706  * 
36707  * @constructor
36708  * Create a new Brick
36709  * @param {Object} config The config object
36710  */
36711
36712 Roo.bootstrap.Brick = function(config){
36713     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36714     
36715     this.addEvents({
36716         // raw events
36717         /**
36718          * @event click
36719          * When a Brick is click
36720          * @param {Roo.bootstrap.Brick} this
36721          * @param {Roo.EventObject} e
36722          */
36723         "click" : true
36724     });
36725 };
36726
36727 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36728     
36729     /**
36730      * @cfg {String} title
36731      */   
36732     title : '',
36733     /**
36734      * @cfg {String} html
36735      */   
36736     html : '',
36737     /**
36738      * @cfg {String} bgimage
36739      */   
36740     bgimage : '',
36741     /**
36742      * @cfg {String} cls
36743      */   
36744     cls : '',
36745     /**
36746      * @cfg {String} href
36747      */   
36748     href : '',
36749     /**
36750      * @cfg {String} video
36751      */   
36752     video : '',
36753     /**
36754      * @cfg {Boolean} square
36755      */   
36756     square : true,
36757     
36758     getAutoCreate : function()
36759     {
36760         var cls = 'roo-brick';
36761         
36762         if(this.href.length){
36763             cls += ' roo-brick-link';
36764         }
36765         
36766         if(this.bgimage.length){
36767             cls += ' roo-brick-image';
36768         }
36769         
36770         if(!this.html.length && !this.bgimage.length){
36771             cls += ' roo-brick-center-title';
36772         }
36773         
36774         if(!this.html.length && this.bgimage.length){
36775             cls += ' roo-brick-bottom-title';
36776         }
36777         
36778         if(this.cls){
36779             cls += ' ' + this.cls;
36780         }
36781         
36782         var cfg = {
36783             tag: (this.href.length) ? 'a' : 'div',
36784             cls: cls,
36785             cn: [
36786                 {
36787                     tag: 'div',
36788                     cls: 'roo-brick-paragraph',
36789                     cn: []
36790                 }
36791             ]
36792         };
36793         
36794         if(this.href.length){
36795             cfg.href = this.href;
36796         }
36797         
36798         var cn = cfg.cn[0].cn;
36799         
36800         if(this.title.length){
36801             cn.push({
36802                 tag: 'h4',
36803                 cls: 'roo-brick-title',
36804                 html: this.title
36805             });
36806         }
36807         
36808         if(this.html.length){
36809             cn.push({
36810                 tag: 'p',
36811                 cls: 'roo-brick-text',
36812                 html: this.html
36813             });
36814         } else {
36815             cn.cls += ' hide';
36816         }
36817         
36818         if(this.bgimage.length){
36819             cfg.cn.push({
36820                 tag: 'img',
36821                 cls: 'roo-brick-image-view',
36822                 src: this.bgimage
36823             });
36824         }
36825         
36826         return cfg;
36827     },
36828     
36829     initEvents: function() 
36830     {
36831         if(this.title.length || this.html.length){
36832             this.el.on('mouseenter'  ,this.enter, this);
36833             this.el.on('mouseleave', this.leave, this);
36834         }
36835         
36836         Roo.EventManager.onWindowResize(this.resize, this); 
36837         
36838         if(this.bgimage.length){
36839             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36840             this.imageEl.on('load', this.onImageLoad, this);
36841             return;
36842         }
36843         
36844         this.resize();
36845     },
36846     
36847     onImageLoad : function()
36848     {
36849         this.resize();
36850     },
36851     
36852     resize : function()
36853     {
36854         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36855         
36856         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36857         
36858         if(this.bgimage.length){
36859             var image = this.el.select('.roo-brick-image-view', true).first();
36860             
36861             image.setWidth(paragraph.getWidth());
36862             
36863             if(this.square){
36864                 image.setHeight(paragraph.getWidth());
36865             }
36866             
36867             this.el.setHeight(image.getHeight());
36868             paragraph.setHeight(image.getHeight());
36869             
36870         }
36871         
36872     },
36873     
36874     enter: function(e, el)
36875     {
36876         e.preventDefault();
36877         
36878         if(this.bgimage.length){
36879             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36880             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36881         }
36882     },
36883     
36884     leave: function(e, el)
36885     {
36886         e.preventDefault();
36887         
36888         if(this.bgimage.length){
36889             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36890             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36891         }
36892     }
36893     
36894 });
36895
36896  
36897
36898  /*
36899  * - LGPL
36900  *
36901  * Number field 
36902  */
36903
36904 /**
36905  * @class Roo.bootstrap.NumberField
36906  * @extends Roo.bootstrap.Input
36907  * Bootstrap NumberField class
36908  * 
36909  * 
36910  * 
36911  * 
36912  * @constructor
36913  * Create a new NumberField
36914  * @param {Object} config The config object
36915  */
36916
36917 Roo.bootstrap.NumberField = function(config){
36918     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36919 };
36920
36921 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36922     
36923     /**
36924      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36925      */
36926     allowDecimals : true,
36927     /**
36928      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36929      */
36930     decimalSeparator : ".",
36931     /**
36932      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36933      */
36934     decimalPrecision : 2,
36935     /**
36936      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36937      */
36938     allowNegative : true,
36939     
36940     /**
36941      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36942      */
36943     allowZero: true,
36944     /**
36945      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36946      */
36947     minValue : Number.NEGATIVE_INFINITY,
36948     /**
36949      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36950      */
36951     maxValue : Number.MAX_VALUE,
36952     /**
36953      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36954      */
36955     minText : "The minimum value for this field is {0}",
36956     /**
36957      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36958      */
36959     maxText : "The maximum value for this field is {0}",
36960     /**
36961      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36962      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36963      */
36964     nanText : "{0} is not a valid number",
36965     /**
36966      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36967      */
36968     thousandsDelimiter : false,
36969     /**
36970      * @cfg {String} valueAlign alignment of value
36971      */
36972     valueAlign : "left",
36973
36974     getAutoCreate : function()
36975     {
36976         var hiddenInput = {
36977             tag: 'input',
36978             type: 'hidden',
36979             id: Roo.id(),
36980             cls: 'hidden-number-input'
36981         };
36982         
36983         if (this.name) {
36984             hiddenInput.name = this.name;
36985         }
36986         
36987         this.name = '';
36988         
36989         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36990         
36991         this.name = hiddenInput.name;
36992         
36993         if(cfg.cn.length > 0) {
36994             cfg.cn.push(hiddenInput);
36995         }
36996         
36997         return cfg;
36998     },
36999
37000     // private
37001     initEvents : function()
37002     {   
37003         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37004         
37005         var allowed = "0123456789";
37006         
37007         if(this.allowDecimals){
37008             allowed += this.decimalSeparator;
37009         }
37010         
37011         if(this.allowNegative){
37012             allowed += "-";
37013         }
37014         
37015         if(this.thousandsDelimiter) {
37016             allowed += ",";
37017         }
37018         
37019         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37020         
37021         var keyPress = function(e){
37022             
37023             var k = e.getKey();
37024             
37025             var c = e.getCharCode();
37026             
37027             if(
37028                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37029                     allowed.indexOf(String.fromCharCode(c)) === -1
37030             ){
37031                 e.stopEvent();
37032                 return;
37033             }
37034             
37035             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37036                 return;
37037             }
37038             
37039             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37040                 e.stopEvent();
37041             }
37042         };
37043         
37044         this.el.on("keypress", keyPress, this);
37045     },
37046     
37047     validateValue : function(value)
37048     {
37049         
37050         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37051             return false;
37052         }
37053         
37054         var num = this.parseValue(value);
37055         
37056         if(isNaN(num)){
37057             this.markInvalid(String.format(this.nanText, value));
37058             return false;
37059         }
37060         
37061         if(num < this.minValue){
37062             this.markInvalid(String.format(this.minText, this.minValue));
37063             return false;
37064         }
37065         
37066         if(num > this.maxValue){
37067             this.markInvalid(String.format(this.maxText, this.maxValue));
37068             return false;
37069         }
37070         
37071         return true;
37072     },
37073
37074     getValue : function()
37075     {
37076         var v = this.hiddenEl().getValue();
37077         
37078         return this.fixPrecision(this.parseValue(v));
37079     },
37080
37081     parseValue : function(value)
37082     {
37083         if(this.thousandsDelimiter) {
37084             value += "";
37085             r = new RegExp(",", "g");
37086             value = value.replace(r, "");
37087         }
37088         
37089         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37090         return isNaN(value) ? '' : value;
37091     },
37092
37093     fixPrecision : function(value)
37094     {
37095         if(this.thousandsDelimiter) {
37096             value += "";
37097             r = new RegExp(",", "g");
37098             value = value.replace(r, "");
37099         }
37100         
37101         var nan = isNaN(value);
37102         
37103         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37104             return nan ? '' : value;
37105         }
37106         return parseFloat(value).toFixed(this.decimalPrecision);
37107     },
37108
37109     setValue : function(v)
37110     {
37111         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37112         
37113         this.value = v;
37114         
37115         if(this.rendered){
37116             
37117             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37118             
37119             this.inputEl().dom.value = (v == '') ? '' :
37120                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37121             
37122             if(!this.allowZero && v === '0') {
37123                 this.hiddenEl().dom.value = '';
37124                 this.inputEl().dom.value = '';
37125             }
37126             
37127             this.validate();
37128         }
37129     },
37130
37131     decimalPrecisionFcn : function(v)
37132     {
37133         return Math.floor(v);
37134     },
37135
37136     beforeBlur : function()
37137     {
37138         var v = this.parseValue(this.getRawValue());
37139         
37140         if(v || v === 0 || v === ''){
37141             this.setValue(v);
37142         }
37143     },
37144     
37145     hiddenEl : function()
37146     {
37147         return this.el.select('input.hidden-number-input',true).first();
37148     }
37149     
37150 });
37151
37152  
37153
37154 /*
37155 * Licence: LGPL
37156 */
37157
37158 /**
37159  * @class Roo.bootstrap.DocumentSlider
37160  * @extends Roo.bootstrap.Component
37161  * Bootstrap DocumentSlider class
37162  * 
37163  * @constructor
37164  * Create a new DocumentViewer
37165  * @param {Object} config The config object
37166  */
37167
37168 Roo.bootstrap.DocumentSlider = function(config){
37169     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37170     
37171     this.files = [];
37172     
37173     this.addEvents({
37174         /**
37175          * @event initial
37176          * Fire after initEvent
37177          * @param {Roo.bootstrap.DocumentSlider} this
37178          */
37179         "initial" : true,
37180         /**
37181          * @event update
37182          * Fire after update
37183          * @param {Roo.bootstrap.DocumentSlider} this
37184          */
37185         "update" : true,
37186         /**
37187          * @event click
37188          * Fire after click
37189          * @param {Roo.bootstrap.DocumentSlider} this
37190          */
37191         "click" : true
37192     });
37193 };
37194
37195 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37196     
37197     files : false,
37198     
37199     indicator : 0,
37200     
37201     getAutoCreate : function()
37202     {
37203         var cfg = {
37204             tag : 'div',
37205             cls : 'roo-document-slider',
37206             cn : [
37207                 {
37208                     tag : 'div',
37209                     cls : 'roo-document-slider-header',
37210                     cn : [
37211                         {
37212                             tag : 'div',
37213                             cls : 'roo-document-slider-header-title'
37214                         }
37215                     ]
37216                 },
37217                 {
37218                     tag : 'div',
37219                     cls : 'roo-document-slider-body',
37220                     cn : [
37221                         {
37222                             tag : 'div',
37223                             cls : 'roo-document-slider-prev',
37224                             cn : [
37225                                 {
37226                                     tag : 'i',
37227                                     cls : 'fa fa-chevron-left'
37228                                 }
37229                             ]
37230                         },
37231                         {
37232                             tag : 'div',
37233                             cls : 'roo-document-slider-thumb',
37234                             cn : [
37235                                 {
37236                                     tag : 'img',
37237                                     cls : 'roo-document-slider-image'
37238                                 }
37239                             ]
37240                         },
37241                         {
37242                             tag : 'div',
37243                             cls : 'roo-document-slider-next',
37244                             cn : [
37245                                 {
37246                                     tag : 'i',
37247                                     cls : 'fa fa-chevron-right'
37248                                 }
37249                             ]
37250                         }
37251                     ]
37252                 }
37253             ]
37254         };
37255         
37256         return cfg;
37257     },
37258     
37259     initEvents : function()
37260     {
37261         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37262         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37263         
37264         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37265         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37266         
37267         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37268         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37269         
37270         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37271         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37272         
37273         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37274         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37275         
37276         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37277         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37278         
37279         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37280         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37281         
37282         this.thumbEl.on('click', this.onClick, this);
37283         
37284         this.prevIndicator.on('click', this.prev, this);
37285         
37286         this.nextIndicator.on('click', this.next, this);
37287         
37288     },
37289     
37290     initial : function()
37291     {
37292         if(this.files.length){
37293             this.indicator = 1;
37294             this.update()
37295         }
37296         
37297         this.fireEvent('initial', this);
37298     },
37299     
37300     update : function()
37301     {
37302         this.imageEl.attr('src', this.files[this.indicator - 1]);
37303         
37304         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37305         
37306         this.prevIndicator.show();
37307         
37308         if(this.indicator == 1){
37309             this.prevIndicator.hide();
37310         }
37311         
37312         this.nextIndicator.show();
37313         
37314         if(this.indicator == this.files.length){
37315             this.nextIndicator.hide();
37316         }
37317         
37318         this.thumbEl.scrollTo('top');
37319         
37320         this.fireEvent('update', this);
37321     },
37322     
37323     onClick : function(e)
37324     {
37325         e.preventDefault();
37326         
37327         this.fireEvent('click', this);
37328     },
37329     
37330     prev : function(e)
37331     {
37332         e.preventDefault();
37333         
37334         this.indicator = Math.max(1, this.indicator - 1);
37335         
37336         this.update();
37337     },
37338     
37339     next : function(e)
37340     {
37341         e.preventDefault();
37342         
37343         this.indicator = Math.min(this.files.length, this.indicator + 1);
37344         
37345         this.update();
37346     }
37347 });
37348 /*
37349  * - LGPL
37350  *
37351  * RadioSet
37352  *
37353  *
37354  */
37355
37356 /**
37357  * @class Roo.bootstrap.RadioSet
37358  * @extends Roo.bootstrap.Input
37359  * Bootstrap RadioSet class
37360  * @cfg {String} indicatorpos (left|right) default left
37361  * @cfg {Boolean} inline (true|false) inline the element (default true)
37362  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37363  * @constructor
37364  * Create a new RadioSet
37365  * @param {Object} config The config object
37366  */
37367
37368 Roo.bootstrap.RadioSet = function(config){
37369     
37370     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37371     
37372     this.radioes = [];
37373     
37374     Roo.bootstrap.RadioSet.register(this);
37375     
37376     this.addEvents({
37377         /**
37378         * @event check
37379         * Fires when the element is checked or unchecked.
37380         * @param {Roo.bootstrap.RadioSet} this This radio
37381         * @param {Roo.bootstrap.Radio} item The checked item
37382         */
37383        check : true,
37384        /**
37385         * @event click
37386         * Fires when the element is click.
37387         * @param {Roo.bootstrap.RadioSet} this This radio set
37388         * @param {Roo.bootstrap.Radio} item The checked item
37389         * @param {Roo.EventObject} e The event object
37390         */
37391        click : true
37392     });
37393     
37394 };
37395
37396 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37397
37398     radioes : false,
37399     
37400     inline : true,
37401     
37402     weight : '',
37403     
37404     indicatorpos : 'left',
37405     
37406     getAutoCreate : function()
37407     {
37408         var label = {
37409             tag : 'label',
37410             cls : 'roo-radio-set-label',
37411             cn : [
37412                 {
37413                     tag : 'span',
37414                     html : this.fieldLabel
37415                 }
37416             ]
37417         };
37418         if (Roo.bootstrap.version == 3) {
37419             
37420             
37421             if(this.indicatorpos == 'left'){
37422                 label.cn.unshift({
37423                     tag : 'i',
37424                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37425                     tooltip : 'This field is required'
37426                 });
37427             } else {
37428                 label.cn.push({
37429                     tag : 'i',
37430                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37431                     tooltip : 'This field is required'
37432                 });
37433             }
37434         }
37435         var items = {
37436             tag : 'div',
37437             cls : 'roo-radio-set-items'
37438         };
37439         
37440         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37441         
37442         if (align === 'left' && this.fieldLabel.length) {
37443             
37444             items = {
37445                 cls : "roo-radio-set-right", 
37446                 cn: [
37447                     items
37448                 ]
37449             };
37450             
37451             if(this.labelWidth > 12){
37452                 label.style = "width: " + this.labelWidth + 'px';
37453             }
37454             
37455             if(this.labelWidth < 13 && this.labelmd == 0){
37456                 this.labelmd = this.labelWidth;
37457             }
37458             
37459             if(this.labellg > 0){
37460                 label.cls += ' col-lg-' + this.labellg;
37461                 items.cls += ' col-lg-' + (12 - this.labellg);
37462             }
37463             
37464             if(this.labelmd > 0){
37465                 label.cls += ' col-md-' + this.labelmd;
37466                 items.cls += ' col-md-' + (12 - this.labelmd);
37467             }
37468             
37469             if(this.labelsm > 0){
37470                 label.cls += ' col-sm-' + this.labelsm;
37471                 items.cls += ' col-sm-' + (12 - this.labelsm);
37472             }
37473             
37474             if(this.labelxs > 0){
37475                 label.cls += ' col-xs-' + this.labelxs;
37476                 items.cls += ' col-xs-' + (12 - this.labelxs);
37477             }
37478         }
37479         
37480         var cfg = {
37481             tag : 'div',
37482             cls : 'roo-radio-set',
37483             cn : [
37484                 {
37485                     tag : 'input',
37486                     cls : 'roo-radio-set-input',
37487                     type : 'hidden',
37488                     name : this.name,
37489                     value : this.value ? this.value :  ''
37490                 },
37491                 label,
37492                 items
37493             ]
37494         };
37495         
37496         if(this.weight.length){
37497             cfg.cls += ' roo-radio-' + this.weight;
37498         }
37499         
37500         if(this.inline) {
37501             cfg.cls += ' roo-radio-set-inline';
37502         }
37503         
37504         var settings=this;
37505         ['xs','sm','md','lg'].map(function(size){
37506             if (settings[size]) {
37507                 cfg.cls += ' col-' + size + '-' + settings[size];
37508             }
37509         });
37510         
37511         return cfg;
37512         
37513     },
37514
37515     initEvents : function()
37516     {
37517         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37518         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37519         
37520         if(!this.fieldLabel.length){
37521             this.labelEl.hide();
37522         }
37523         
37524         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37525         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37526         
37527         this.indicator = this.indicatorEl();
37528         
37529         if(this.indicator){
37530             this.indicator.addClass('invisible');
37531         }
37532         
37533         this.originalValue = this.getValue();
37534         
37535     },
37536     
37537     inputEl: function ()
37538     {
37539         return this.el.select('.roo-radio-set-input', true).first();
37540     },
37541     
37542     getChildContainer : function()
37543     {
37544         return this.itemsEl;
37545     },
37546     
37547     register : function(item)
37548     {
37549         this.radioes.push(item);
37550         
37551     },
37552     
37553     validate : function()
37554     {   
37555         if(this.getVisibilityEl().hasClass('hidden')){
37556             return true;
37557         }
37558         
37559         var valid = false;
37560         
37561         Roo.each(this.radioes, function(i){
37562             if(!i.checked){
37563                 return;
37564             }
37565             
37566             valid = true;
37567             return false;
37568         });
37569         
37570         if(this.allowBlank) {
37571             return true;
37572         }
37573         
37574         if(this.disabled || valid){
37575             this.markValid();
37576             return true;
37577         }
37578         
37579         this.markInvalid();
37580         return false;
37581         
37582     },
37583     
37584     markValid : function()
37585     {
37586         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37587             this.indicatorEl().removeClass('visible');
37588             this.indicatorEl().addClass('invisible');
37589         }
37590         
37591         
37592         if (Roo.bootstrap.version == 3) {
37593             this.el.removeClass([this.invalidClass, this.validClass]);
37594             this.el.addClass(this.validClass);
37595         } else {
37596             this.el.removeClass(['is-invalid','is-valid']);
37597             this.el.addClass(['is-valid']);
37598         }
37599         this.fireEvent('valid', this);
37600     },
37601     
37602     markInvalid : function(msg)
37603     {
37604         if(this.allowBlank || this.disabled){
37605             return;
37606         }
37607         
37608         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37609             this.indicatorEl().removeClass('invisible');
37610             this.indicatorEl().addClass('visible');
37611         }
37612         if (Roo.bootstrap.version == 3) {
37613             this.el.removeClass([this.invalidClass, this.validClass]);
37614             this.el.addClass(this.invalidClass);
37615         } else {
37616             this.el.removeClass(['is-invalid','is-valid']);
37617             this.el.addClass(['is-invalid']);
37618         }
37619         
37620         this.fireEvent('invalid', this, msg);
37621         
37622     },
37623     
37624     setValue : function(v, suppressEvent)
37625     {   
37626         if(this.value === v){
37627             return;
37628         }
37629         
37630         this.value = v;
37631         
37632         if(this.rendered){
37633             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37634         }
37635         
37636         Roo.each(this.radioes, function(i){
37637             i.checked = false;
37638             i.el.removeClass('checked');
37639         });
37640         
37641         Roo.each(this.radioes, function(i){
37642             
37643             if(i.value === v || i.value.toString() === v.toString()){
37644                 i.checked = true;
37645                 i.el.addClass('checked');
37646                 
37647                 if(suppressEvent !== true){
37648                     this.fireEvent('check', this, i);
37649                 }
37650                 
37651                 return false;
37652             }
37653             
37654         }, this);
37655         
37656         this.validate();
37657     },
37658     
37659     clearInvalid : function(){
37660         
37661         if(!this.el || this.preventMark){
37662             return;
37663         }
37664         
37665         this.el.removeClass([this.invalidClass]);
37666         
37667         this.fireEvent('valid', this);
37668     }
37669     
37670 });
37671
37672 Roo.apply(Roo.bootstrap.RadioSet, {
37673     
37674     groups: {},
37675     
37676     register : function(set)
37677     {
37678         this.groups[set.name] = set;
37679     },
37680     
37681     get: function(name) 
37682     {
37683         if (typeof(this.groups[name]) == 'undefined') {
37684             return false;
37685         }
37686         
37687         return this.groups[name] ;
37688     }
37689     
37690 });
37691 /*
37692  * Based on:
37693  * Ext JS Library 1.1.1
37694  * Copyright(c) 2006-2007, Ext JS, LLC.
37695  *
37696  * Originally Released Under LGPL - original licence link has changed is not relivant.
37697  *
37698  * Fork - LGPL
37699  * <script type="text/javascript">
37700  */
37701
37702
37703 /**
37704  * @class Roo.bootstrap.SplitBar
37705  * @extends Roo.util.Observable
37706  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37707  * <br><br>
37708  * Usage:
37709  * <pre><code>
37710 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37711                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37712 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37713 split.minSize = 100;
37714 split.maxSize = 600;
37715 split.animate = true;
37716 split.on('moved', splitterMoved);
37717 </code></pre>
37718  * @constructor
37719  * Create a new SplitBar
37720  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37721  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37722  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37723  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37724                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37725                         position of the SplitBar).
37726  */
37727 Roo.bootstrap.SplitBar = function(cfg){
37728     
37729     /** @private */
37730     
37731     //{
37732     //  dragElement : elm
37733     //  resizingElement: el,
37734         // optional..
37735     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37736     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37737         // existingProxy ???
37738     //}
37739     
37740     this.el = Roo.get(cfg.dragElement, true);
37741     this.el.dom.unselectable = "on";
37742     /** @private */
37743     this.resizingEl = Roo.get(cfg.resizingElement, true);
37744
37745     /**
37746      * @private
37747      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37748      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37749      * @type Number
37750      */
37751     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37752     
37753     /**
37754      * The minimum size of the resizing element. (Defaults to 0)
37755      * @type Number
37756      */
37757     this.minSize = 0;
37758     
37759     /**
37760      * The maximum size of the resizing element. (Defaults to 2000)
37761      * @type Number
37762      */
37763     this.maxSize = 2000;
37764     
37765     /**
37766      * Whether to animate the transition to the new size
37767      * @type Boolean
37768      */
37769     this.animate = false;
37770     
37771     /**
37772      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37773      * @type Boolean
37774      */
37775     this.useShim = false;
37776     
37777     /** @private */
37778     this.shim = null;
37779     
37780     if(!cfg.existingProxy){
37781         /** @private */
37782         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37783     }else{
37784         this.proxy = Roo.get(cfg.existingProxy).dom;
37785     }
37786     /** @private */
37787     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37788     
37789     /** @private */
37790     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37791     
37792     /** @private */
37793     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37794     
37795     /** @private */
37796     this.dragSpecs = {};
37797     
37798     /**
37799      * @private The adapter to use to positon and resize elements
37800      */
37801     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37802     this.adapter.init(this);
37803     
37804     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37805         /** @private */
37806         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37807         this.el.addClass("roo-splitbar-h");
37808     }else{
37809         /** @private */
37810         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37811         this.el.addClass("roo-splitbar-v");
37812     }
37813     
37814     this.addEvents({
37815         /**
37816          * @event resize
37817          * Fires when the splitter is moved (alias for {@link #event-moved})
37818          * @param {Roo.bootstrap.SplitBar} this
37819          * @param {Number} newSize the new width or height
37820          */
37821         "resize" : true,
37822         /**
37823          * @event moved
37824          * Fires when the splitter is moved
37825          * @param {Roo.bootstrap.SplitBar} this
37826          * @param {Number} newSize the new width or height
37827          */
37828         "moved" : true,
37829         /**
37830          * @event beforeresize
37831          * Fires before the splitter is dragged
37832          * @param {Roo.bootstrap.SplitBar} this
37833          */
37834         "beforeresize" : true,
37835
37836         "beforeapply" : true
37837     });
37838
37839     Roo.util.Observable.call(this);
37840 };
37841
37842 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37843     onStartProxyDrag : function(x, y){
37844         this.fireEvent("beforeresize", this);
37845         if(!this.overlay){
37846             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37847             o.unselectable();
37848             o.enableDisplayMode("block");
37849             // all splitbars share the same overlay
37850             Roo.bootstrap.SplitBar.prototype.overlay = o;
37851         }
37852         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37853         this.overlay.show();
37854         Roo.get(this.proxy).setDisplayed("block");
37855         var size = this.adapter.getElementSize(this);
37856         this.activeMinSize = this.getMinimumSize();;
37857         this.activeMaxSize = this.getMaximumSize();;
37858         var c1 = size - this.activeMinSize;
37859         var c2 = Math.max(this.activeMaxSize - size, 0);
37860         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37861             this.dd.resetConstraints();
37862             this.dd.setXConstraint(
37863                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37864                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37865             );
37866             this.dd.setYConstraint(0, 0);
37867         }else{
37868             this.dd.resetConstraints();
37869             this.dd.setXConstraint(0, 0);
37870             this.dd.setYConstraint(
37871                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37872                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37873             );
37874          }
37875         this.dragSpecs.startSize = size;
37876         this.dragSpecs.startPoint = [x, y];
37877         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37878     },
37879     
37880     /** 
37881      * @private Called after the drag operation by the DDProxy
37882      */
37883     onEndProxyDrag : function(e){
37884         Roo.get(this.proxy).setDisplayed(false);
37885         var endPoint = Roo.lib.Event.getXY(e);
37886         if(this.overlay){
37887             this.overlay.hide();
37888         }
37889         var newSize;
37890         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37891             newSize = this.dragSpecs.startSize + 
37892                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37893                     endPoint[0] - this.dragSpecs.startPoint[0] :
37894                     this.dragSpecs.startPoint[0] - endPoint[0]
37895                 );
37896         }else{
37897             newSize = this.dragSpecs.startSize + 
37898                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37899                     endPoint[1] - this.dragSpecs.startPoint[1] :
37900                     this.dragSpecs.startPoint[1] - endPoint[1]
37901                 );
37902         }
37903         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37904         if(newSize != this.dragSpecs.startSize){
37905             if(this.fireEvent('beforeapply', this, newSize) !== false){
37906                 this.adapter.setElementSize(this, newSize);
37907                 this.fireEvent("moved", this, newSize);
37908                 this.fireEvent("resize", this, newSize);
37909             }
37910         }
37911     },
37912     
37913     /**
37914      * Get the adapter this SplitBar uses
37915      * @return The adapter object
37916      */
37917     getAdapter : function(){
37918         return this.adapter;
37919     },
37920     
37921     /**
37922      * Set the adapter this SplitBar uses
37923      * @param {Object} adapter A SplitBar adapter object
37924      */
37925     setAdapter : function(adapter){
37926         this.adapter = adapter;
37927         this.adapter.init(this);
37928     },
37929     
37930     /**
37931      * Gets the minimum size for the resizing element
37932      * @return {Number} The minimum size
37933      */
37934     getMinimumSize : function(){
37935         return this.minSize;
37936     },
37937     
37938     /**
37939      * Sets the minimum size for the resizing element
37940      * @param {Number} minSize The minimum size
37941      */
37942     setMinimumSize : function(minSize){
37943         this.minSize = minSize;
37944     },
37945     
37946     /**
37947      * Gets the maximum size for the resizing element
37948      * @return {Number} The maximum size
37949      */
37950     getMaximumSize : function(){
37951         return this.maxSize;
37952     },
37953     
37954     /**
37955      * Sets the maximum size for the resizing element
37956      * @param {Number} maxSize The maximum size
37957      */
37958     setMaximumSize : function(maxSize){
37959         this.maxSize = maxSize;
37960     },
37961     
37962     /**
37963      * Sets the initialize size for the resizing element
37964      * @param {Number} size The initial size
37965      */
37966     setCurrentSize : function(size){
37967         var oldAnimate = this.animate;
37968         this.animate = false;
37969         this.adapter.setElementSize(this, size);
37970         this.animate = oldAnimate;
37971     },
37972     
37973     /**
37974      * Destroy this splitbar. 
37975      * @param {Boolean} removeEl True to remove the element
37976      */
37977     destroy : function(removeEl){
37978         if(this.shim){
37979             this.shim.remove();
37980         }
37981         this.dd.unreg();
37982         this.proxy.parentNode.removeChild(this.proxy);
37983         if(removeEl){
37984             this.el.remove();
37985         }
37986     }
37987 });
37988
37989 /**
37990  * @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.
37991  */
37992 Roo.bootstrap.SplitBar.createProxy = function(dir){
37993     var proxy = new Roo.Element(document.createElement("div"));
37994     proxy.unselectable();
37995     var cls = 'roo-splitbar-proxy';
37996     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37997     document.body.appendChild(proxy.dom);
37998     return proxy.dom;
37999 };
38000
38001 /** 
38002  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38003  * Default Adapter. It assumes the splitter and resizing element are not positioned
38004  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38005  */
38006 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38007 };
38008
38009 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38010     // do nothing for now
38011     init : function(s){
38012     
38013     },
38014     /**
38015      * Called before drag operations to get the current size of the resizing element. 
38016      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38017      */
38018      getElementSize : function(s){
38019         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38020             return s.resizingEl.getWidth();
38021         }else{
38022             return s.resizingEl.getHeight();
38023         }
38024     },
38025     
38026     /**
38027      * Called after drag operations to set the size of the resizing element.
38028      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38029      * @param {Number} newSize The new size to set
38030      * @param {Function} onComplete A function to be invoked when resizing is complete
38031      */
38032     setElementSize : function(s, newSize, onComplete){
38033         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38034             if(!s.animate){
38035                 s.resizingEl.setWidth(newSize);
38036                 if(onComplete){
38037                     onComplete(s, newSize);
38038                 }
38039             }else{
38040                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38041             }
38042         }else{
38043             
38044             if(!s.animate){
38045                 s.resizingEl.setHeight(newSize);
38046                 if(onComplete){
38047                     onComplete(s, newSize);
38048                 }
38049             }else{
38050                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38051             }
38052         }
38053     }
38054 };
38055
38056 /** 
38057  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38058  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38059  * Adapter that  moves the splitter element to align with the resized sizing element. 
38060  * Used with an absolute positioned SplitBar.
38061  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38062  * document.body, make sure you assign an id to the body element.
38063  */
38064 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38065     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38066     this.container = Roo.get(container);
38067 };
38068
38069 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38070     init : function(s){
38071         this.basic.init(s);
38072     },
38073     
38074     getElementSize : function(s){
38075         return this.basic.getElementSize(s);
38076     },
38077     
38078     setElementSize : function(s, newSize, onComplete){
38079         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38080     },
38081     
38082     moveSplitter : function(s){
38083         var yes = Roo.bootstrap.SplitBar;
38084         switch(s.placement){
38085             case yes.LEFT:
38086                 s.el.setX(s.resizingEl.getRight());
38087                 break;
38088             case yes.RIGHT:
38089                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38090                 break;
38091             case yes.TOP:
38092                 s.el.setY(s.resizingEl.getBottom());
38093                 break;
38094             case yes.BOTTOM:
38095                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38096                 break;
38097         }
38098     }
38099 };
38100
38101 /**
38102  * Orientation constant - Create a vertical SplitBar
38103  * @static
38104  * @type Number
38105  */
38106 Roo.bootstrap.SplitBar.VERTICAL = 1;
38107
38108 /**
38109  * Orientation constant - Create a horizontal SplitBar
38110  * @static
38111  * @type Number
38112  */
38113 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38114
38115 /**
38116  * Placement constant - The resizing element is to the left of the splitter element
38117  * @static
38118  * @type Number
38119  */
38120 Roo.bootstrap.SplitBar.LEFT = 1;
38121
38122 /**
38123  * Placement constant - The resizing element is to the right of the splitter element
38124  * @static
38125  * @type Number
38126  */
38127 Roo.bootstrap.SplitBar.RIGHT = 2;
38128
38129 /**
38130  * Placement constant - The resizing element is positioned above the splitter element
38131  * @static
38132  * @type Number
38133  */
38134 Roo.bootstrap.SplitBar.TOP = 3;
38135
38136 /**
38137  * Placement constant - The resizing element is positioned under splitter element
38138  * @static
38139  * @type Number
38140  */
38141 Roo.bootstrap.SplitBar.BOTTOM = 4;
38142 Roo.namespace("Roo.bootstrap.layout");/*
38143  * Based on:
38144  * Ext JS Library 1.1.1
38145  * Copyright(c) 2006-2007, Ext JS, LLC.
38146  *
38147  * Originally Released Under LGPL - original licence link has changed is not relivant.
38148  *
38149  * Fork - LGPL
38150  * <script type="text/javascript">
38151  */
38152
38153 /**
38154  * @class Roo.bootstrap.layout.Manager
38155  * @extends Roo.bootstrap.Component
38156  * Base class for layout managers.
38157  */
38158 Roo.bootstrap.layout.Manager = function(config)
38159 {
38160     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38161
38162
38163
38164
38165
38166     /** false to disable window resize monitoring @type Boolean */
38167     this.monitorWindowResize = true;
38168     this.regions = {};
38169     this.addEvents({
38170         /**
38171          * @event layout
38172          * Fires when a layout is performed.
38173          * @param {Roo.LayoutManager} this
38174          */
38175         "layout" : true,
38176         /**
38177          * @event regionresized
38178          * Fires when the user resizes a region.
38179          * @param {Roo.LayoutRegion} region The resized region
38180          * @param {Number} newSize The new size (width for east/west, height for north/south)
38181          */
38182         "regionresized" : true,
38183         /**
38184          * @event regioncollapsed
38185          * Fires when a region is collapsed.
38186          * @param {Roo.LayoutRegion} region The collapsed region
38187          */
38188         "regioncollapsed" : true,
38189         /**
38190          * @event regionexpanded
38191          * Fires when a region is expanded.
38192          * @param {Roo.LayoutRegion} region The expanded region
38193          */
38194         "regionexpanded" : true
38195     });
38196     this.updating = false;
38197
38198     if (config.el) {
38199         this.el = Roo.get(config.el);
38200         this.initEvents();
38201     }
38202
38203 };
38204
38205 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38206
38207
38208     regions : null,
38209
38210     monitorWindowResize : true,
38211
38212
38213     updating : false,
38214
38215
38216     onRender : function(ct, position)
38217     {
38218         if(!this.el){
38219             this.el = Roo.get(ct);
38220             this.initEvents();
38221         }
38222         //this.fireEvent('render',this);
38223     },
38224
38225
38226     initEvents: function()
38227     {
38228
38229
38230         // ie scrollbar fix
38231         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38232             document.body.scroll = "no";
38233         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38234             this.el.position('relative');
38235         }
38236         this.id = this.el.id;
38237         this.el.addClass("roo-layout-container");
38238         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38239         if(this.el.dom != document.body ) {
38240             this.el.on('resize', this.layout,this);
38241             this.el.on('show', this.layout,this);
38242         }
38243
38244     },
38245
38246     /**
38247      * Returns true if this layout is currently being updated
38248      * @return {Boolean}
38249      */
38250     isUpdating : function(){
38251         return this.updating;
38252     },
38253
38254     /**
38255      * Suspend the LayoutManager from doing auto-layouts while
38256      * making multiple add or remove calls
38257      */
38258     beginUpdate : function(){
38259         this.updating = true;
38260     },
38261
38262     /**
38263      * Restore auto-layouts and optionally disable the manager from performing a layout
38264      * @param {Boolean} noLayout true to disable a layout update
38265      */
38266     endUpdate : function(noLayout){
38267         this.updating = false;
38268         if(!noLayout){
38269             this.layout();
38270         }
38271     },
38272
38273     layout: function(){
38274         // abstract...
38275     },
38276
38277     onRegionResized : function(region, newSize){
38278         this.fireEvent("regionresized", region, newSize);
38279         this.layout();
38280     },
38281
38282     onRegionCollapsed : function(region){
38283         this.fireEvent("regioncollapsed", region);
38284     },
38285
38286     onRegionExpanded : function(region){
38287         this.fireEvent("regionexpanded", region);
38288     },
38289
38290     /**
38291      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38292      * performs box-model adjustments.
38293      * @return {Object} The size as an object {width: (the width), height: (the height)}
38294      */
38295     getViewSize : function()
38296     {
38297         var size;
38298         if(this.el.dom != document.body){
38299             size = this.el.getSize();
38300         }else{
38301             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38302         }
38303         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38304         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38305         return size;
38306     },
38307
38308     /**
38309      * Returns the Element this layout is bound to.
38310      * @return {Roo.Element}
38311      */
38312     getEl : function(){
38313         return this.el;
38314     },
38315
38316     /**
38317      * Returns the specified region.
38318      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38319      * @return {Roo.LayoutRegion}
38320      */
38321     getRegion : function(target){
38322         return this.regions[target.toLowerCase()];
38323     },
38324
38325     onWindowResize : function(){
38326         if(this.monitorWindowResize){
38327             this.layout();
38328         }
38329     }
38330 });
38331 /*
38332  * Based on:
38333  * Ext JS Library 1.1.1
38334  * Copyright(c) 2006-2007, Ext JS, LLC.
38335  *
38336  * Originally Released Under LGPL - original licence link has changed is not relivant.
38337  *
38338  * Fork - LGPL
38339  * <script type="text/javascript">
38340  */
38341 /**
38342  * @class Roo.bootstrap.layout.Border
38343  * @extends Roo.bootstrap.layout.Manager
38344  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38345  * please see: examples/bootstrap/nested.html<br><br>
38346  
38347 <b>The container the layout is rendered into can be either the body element or any other element.
38348 If it is not the body element, the container needs to either be an absolute positioned element,
38349 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38350 the container size if it is not the body element.</b>
38351
38352 * @constructor
38353 * Create a new Border
38354 * @param {Object} config Configuration options
38355  */
38356 Roo.bootstrap.layout.Border = function(config){
38357     config = config || {};
38358     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38359     
38360     
38361     
38362     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38363         if(config[region]){
38364             config[region].region = region;
38365             this.addRegion(config[region]);
38366         }
38367     },this);
38368     
38369 };
38370
38371 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38372
38373 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38374     
38375     parent : false, // this might point to a 'nest' or a ???
38376     
38377     /**
38378      * Creates and adds a new region if it doesn't already exist.
38379      * @param {String} target The target region key (north, south, east, west or center).
38380      * @param {Object} config The regions config object
38381      * @return {BorderLayoutRegion} The new region
38382      */
38383     addRegion : function(config)
38384     {
38385         if(!this.regions[config.region]){
38386             var r = this.factory(config);
38387             this.bindRegion(r);
38388         }
38389         return this.regions[config.region];
38390     },
38391
38392     // private (kinda)
38393     bindRegion : function(r){
38394         this.regions[r.config.region] = r;
38395         
38396         r.on("visibilitychange",    this.layout, this);
38397         r.on("paneladded",          this.layout, this);
38398         r.on("panelremoved",        this.layout, this);
38399         r.on("invalidated",         this.layout, this);
38400         r.on("resized",             this.onRegionResized, this);
38401         r.on("collapsed",           this.onRegionCollapsed, this);
38402         r.on("expanded",            this.onRegionExpanded, this);
38403     },
38404
38405     /**
38406      * Performs a layout update.
38407      */
38408     layout : function()
38409     {
38410         if(this.updating) {
38411             return;
38412         }
38413         
38414         // render all the rebions if they have not been done alreayd?
38415         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38416             if(this.regions[region] && !this.regions[region].bodyEl){
38417                 this.regions[region].onRender(this.el)
38418             }
38419         },this);
38420         
38421         var size = this.getViewSize();
38422         var w = size.width;
38423         var h = size.height;
38424         var centerW = w;
38425         var centerH = h;
38426         var centerY = 0;
38427         var centerX = 0;
38428         //var x = 0, y = 0;
38429
38430         var rs = this.regions;
38431         var north = rs["north"];
38432         var south = rs["south"]; 
38433         var west = rs["west"];
38434         var east = rs["east"];
38435         var center = rs["center"];
38436         //if(this.hideOnLayout){ // not supported anymore
38437             //c.el.setStyle("display", "none");
38438         //}
38439         if(north && north.isVisible()){
38440             var b = north.getBox();
38441             var m = north.getMargins();
38442             b.width = w - (m.left+m.right);
38443             b.x = m.left;
38444             b.y = m.top;
38445             centerY = b.height + b.y + m.bottom;
38446             centerH -= centerY;
38447             north.updateBox(this.safeBox(b));
38448         }
38449         if(south && south.isVisible()){
38450             var b = south.getBox();
38451             var m = south.getMargins();
38452             b.width = w - (m.left+m.right);
38453             b.x = m.left;
38454             var totalHeight = (b.height + m.top + m.bottom);
38455             b.y = h - totalHeight + m.top;
38456             centerH -= totalHeight;
38457             south.updateBox(this.safeBox(b));
38458         }
38459         if(west && west.isVisible()){
38460             var b = west.getBox();
38461             var m = west.getMargins();
38462             b.height = centerH - (m.top+m.bottom);
38463             b.x = m.left;
38464             b.y = centerY + m.top;
38465             var totalWidth = (b.width + m.left + m.right);
38466             centerX += totalWidth;
38467             centerW -= totalWidth;
38468             west.updateBox(this.safeBox(b));
38469         }
38470         if(east && east.isVisible()){
38471             var b = east.getBox();
38472             var m = east.getMargins();
38473             b.height = centerH - (m.top+m.bottom);
38474             var totalWidth = (b.width + m.left + m.right);
38475             b.x = w - totalWidth + m.left;
38476             b.y = centerY + m.top;
38477             centerW -= totalWidth;
38478             east.updateBox(this.safeBox(b));
38479         }
38480         if(center){
38481             var m = center.getMargins();
38482             var centerBox = {
38483                 x: centerX + m.left,
38484                 y: centerY + m.top,
38485                 width: centerW - (m.left+m.right),
38486                 height: centerH - (m.top+m.bottom)
38487             };
38488             //if(this.hideOnLayout){
38489                 //center.el.setStyle("display", "block");
38490             //}
38491             center.updateBox(this.safeBox(centerBox));
38492         }
38493         this.el.repaint();
38494         this.fireEvent("layout", this);
38495     },
38496
38497     // private
38498     safeBox : function(box){
38499         box.width = Math.max(0, box.width);
38500         box.height = Math.max(0, box.height);
38501         return box;
38502     },
38503
38504     /**
38505      * Adds a ContentPanel (or subclass) to this layout.
38506      * @param {String} target The target region key (north, south, east, west or center).
38507      * @param {Roo.ContentPanel} panel The panel to add
38508      * @return {Roo.ContentPanel} The added panel
38509      */
38510     add : function(target, panel){
38511          
38512         target = target.toLowerCase();
38513         return this.regions[target].add(panel);
38514     },
38515
38516     /**
38517      * Remove a ContentPanel (or subclass) to this layout.
38518      * @param {String} target The target region key (north, south, east, west or center).
38519      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38520      * @return {Roo.ContentPanel} The removed panel
38521      */
38522     remove : function(target, panel){
38523         target = target.toLowerCase();
38524         return this.regions[target].remove(panel);
38525     },
38526
38527     /**
38528      * Searches all regions for a panel with the specified id
38529      * @param {String} panelId
38530      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38531      */
38532     findPanel : function(panelId){
38533         var rs = this.regions;
38534         for(var target in rs){
38535             if(typeof rs[target] != "function"){
38536                 var p = rs[target].getPanel(panelId);
38537                 if(p){
38538                     return p;
38539                 }
38540             }
38541         }
38542         return null;
38543     },
38544
38545     /**
38546      * Searches all regions for a panel with the specified id and activates (shows) it.
38547      * @param {String/ContentPanel} panelId The panels id or the panel itself
38548      * @return {Roo.ContentPanel} The shown panel or null
38549      */
38550     showPanel : function(panelId) {
38551       var rs = this.regions;
38552       for(var target in rs){
38553          var r = rs[target];
38554          if(typeof r != "function"){
38555             if(r.hasPanel(panelId)){
38556                return r.showPanel(panelId);
38557             }
38558          }
38559       }
38560       return null;
38561    },
38562
38563    /**
38564      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38565      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38566      */
38567    /*
38568     restoreState : function(provider){
38569         if(!provider){
38570             provider = Roo.state.Manager;
38571         }
38572         var sm = new Roo.LayoutStateManager();
38573         sm.init(this, provider);
38574     },
38575 */
38576  
38577  
38578     /**
38579      * Adds a xtype elements to the layout.
38580      * <pre><code>
38581
38582 layout.addxtype({
38583        xtype : 'ContentPanel',
38584        region: 'west',
38585        items: [ .... ]
38586    }
38587 );
38588
38589 layout.addxtype({
38590         xtype : 'NestedLayoutPanel',
38591         region: 'west',
38592         layout: {
38593            center: { },
38594            west: { }   
38595         },
38596         items : [ ... list of content panels or nested layout panels.. ]
38597    }
38598 );
38599 </code></pre>
38600      * @param {Object} cfg Xtype definition of item to add.
38601      */
38602     addxtype : function(cfg)
38603     {
38604         // basically accepts a pannel...
38605         // can accept a layout region..!?!?
38606         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38607         
38608         
38609         // theory?  children can only be panels??
38610         
38611         //if (!cfg.xtype.match(/Panel$/)) {
38612         //    return false;
38613         //}
38614         var ret = false;
38615         
38616         if (typeof(cfg.region) == 'undefined') {
38617             Roo.log("Failed to add Panel, region was not set");
38618             Roo.log(cfg);
38619             return false;
38620         }
38621         var region = cfg.region;
38622         delete cfg.region;
38623         
38624           
38625         var xitems = [];
38626         if (cfg.items) {
38627             xitems = cfg.items;
38628             delete cfg.items;
38629         }
38630         var nb = false;
38631         
38632         if ( region == 'center') {
38633             Roo.log("Center: " + cfg.title);
38634         }
38635         
38636         
38637         switch(cfg.xtype) 
38638         {
38639             case 'Content':  // ContentPanel (el, cfg)
38640             case 'Scroll':  // ContentPanel (el, cfg)
38641             case 'View': 
38642                 cfg.autoCreate = cfg.autoCreate || true;
38643                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38644                 //} else {
38645                 //    var el = this.el.createChild();
38646                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38647                 //}
38648                 
38649                 this.add(region, ret);
38650                 break;
38651             
38652             /*
38653             case 'TreePanel': // our new panel!
38654                 cfg.el = this.el.createChild();
38655                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38656                 this.add(region, ret);
38657                 break;
38658             */
38659             
38660             case 'Nest': 
38661                 // create a new Layout (which is  a Border Layout...
38662                 
38663                 var clayout = cfg.layout;
38664                 clayout.el  = this.el.createChild();
38665                 clayout.items   = clayout.items  || [];
38666                 
38667                 delete cfg.layout;
38668                 
38669                 // replace this exitems with the clayout ones..
38670                 xitems = clayout.items;
38671                  
38672                 // force background off if it's in center...
38673                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38674                     cfg.background = false;
38675                 }
38676                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38677                 
38678                 
38679                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38680                 //console.log('adding nested layout panel '  + cfg.toSource());
38681                 this.add(region, ret);
38682                 nb = {}; /// find first...
38683                 break;
38684             
38685             case 'Grid':
38686                 
38687                 // needs grid and region
38688                 
38689                 //var el = this.getRegion(region).el.createChild();
38690                 /*
38691                  *var el = this.el.createChild();
38692                 // create the grid first...
38693                 cfg.grid.container = el;
38694                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38695                 */
38696                 
38697                 if (region == 'center' && this.active ) {
38698                     cfg.background = false;
38699                 }
38700                 
38701                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38702                 
38703                 this.add(region, ret);
38704                 /*
38705                 if (cfg.background) {
38706                     // render grid on panel activation (if panel background)
38707                     ret.on('activate', function(gp) {
38708                         if (!gp.grid.rendered) {
38709                     //        gp.grid.render(el);
38710                         }
38711                     });
38712                 } else {
38713                   //  cfg.grid.render(el);
38714                 }
38715                 */
38716                 break;
38717            
38718            
38719             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38720                 // it was the old xcomponent building that caused this before.
38721                 // espeically if border is the top element in the tree.
38722                 ret = this;
38723                 break; 
38724                 
38725                     
38726                 
38727                 
38728                 
38729             default:
38730                 /*
38731                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38732                     
38733                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38734                     this.add(region, ret);
38735                 } else {
38736                 */
38737                     Roo.log(cfg);
38738                     throw "Can not add '" + cfg.xtype + "' to Border";
38739                     return null;
38740              
38741                                 
38742              
38743         }
38744         this.beginUpdate();
38745         // add children..
38746         var region = '';
38747         var abn = {};
38748         Roo.each(xitems, function(i)  {
38749             region = nb && i.region ? i.region : false;
38750             
38751             var add = ret.addxtype(i);
38752            
38753             if (region) {
38754                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38755                 if (!i.background) {
38756                     abn[region] = nb[region] ;
38757                 }
38758             }
38759             
38760         });
38761         this.endUpdate();
38762
38763         // make the last non-background panel active..
38764         //if (nb) { Roo.log(abn); }
38765         if (nb) {
38766             
38767             for(var r in abn) {
38768                 region = this.getRegion(r);
38769                 if (region) {
38770                     // tried using nb[r], but it does not work..
38771                      
38772                     region.showPanel(abn[r]);
38773                    
38774                 }
38775             }
38776         }
38777         return ret;
38778         
38779     },
38780     
38781     
38782 // private
38783     factory : function(cfg)
38784     {
38785         
38786         var validRegions = Roo.bootstrap.layout.Border.regions;
38787
38788         var target = cfg.region;
38789         cfg.mgr = this;
38790         
38791         var r = Roo.bootstrap.layout;
38792         Roo.log(target);
38793         switch(target){
38794             case "north":
38795                 return new r.North(cfg);
38796             case "south":
38797                 return new r.South(cfg);
38798             case "east":
38799                 return new r.East(cfg);
38800             case "west":
38801                 return new r.West(cfg);
38802             case "center":
38803                 return new r.Center(cfg);
38804         }
38805         throw 'Layout region "'+target+'" not supported.';
38806     }
38807     
38808     
38809 });
38810  /*
38811  * Based on:
38812  * Ext JS Library 1.1.1
38813  * Copyright(c) 2006-2007, Ext JS, LLC.
38814  *
38815  * Originally Released Under LGPL - original licence link has changed is not relivant.
38816  *
38817  * Fork - LGPL
38818  * <script type="text/javascript">
38819  */
38820  
38821 /**
38822  * @class Roo.bootstrap.layout.Basic
38823  * @extends Roo.util.Observable
38824  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38825  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38826  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38827  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38828  * @cfg {string}   region  the region that it inhabits..
38829  * @cfg {bool}   skipConfig skip config?
38830  * 
38831
38832  */
38833 Roo.bootstrap.layout.Basic = function(config){
38834     
38835     this.mgr = config.mgr;
38836     
38837     this.position = config.region;
38838     
38839     var skipConfig = config.skipConfig;
38840     
38841     this.events = {
38842         /**
38843          * @scope Roo.BasicLayoutRegion
38844          */
38845         
38846         /**
38847          * @event beforeremove
38848          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38849          * @param {Roo.LayoutRegion} this
38850          * @param {Roo.ContentPanel} panel The panel
38851          * @param {Object} e The cancel event object
38852          */
38853         "beforeremove" : true,
38854         /**
38855          * @event invalidated
38856          * Fires when the layout for this region is changed.
38857          * @param {Roo.LayoutRegion} this
38858          */
38859         "invalidated" : true,
38860         /**
38861          * @event visibilitychange
38862          * Fires when this region is shown or hidden 
38863          * @param {Roo.LayoutRegion} this
38864          * @param {Boolean} visibility true or false
38865          */
38866         "visibilitychange" : true,
38867         /**
38868          * @event paneladded
38869          * Fires when a panel is added. 
38870          * @param {Roo.LayoutRegion} this
38871          * @param {Roo.ContentPanel} panel The panel
38872          */
38873         "paneladded" : true,
38874         /**
38875          * @event panelremoved
38876          * Fires when a panel is removed. 
38877          * @param {Roo.LayoutRegion} this
38878          * @param {Roo.ContentPanel} panel The panel
38879          */
38880         "panelremoved" : true,
38881         /**
38882          * @event beforecollapse
38883          * Fires when this region before collapse.
38884          * @param {Roo.LayoutRegion} this
38885          */
38886         "beforecollapse" : true,
38887         /**
38888          * @event collapsed
38889          * Fires when this region is collapsed.
38890          * @param {Roo.LayoutRegion} this
38891          */
38892         "collapsed" : true,
38893         /**
38894          * @event expanded
38895          * Fires when this region is expanded.
38896          * @param {Roo.LayoutRegion} this
38897          */
38898         "expanded" : true,
38899         /**
38900          * @event slideshow
38901          * Fires when this region is slid into view.
38902          * @param {Roo.LayoutRegion} this
38903          */
38904         "slideshow" : true,
38905         /**
38906          * @event slidehide
38907          * Fires when this region slides out of view. 
38908          * @param {Roo.LayoutRegion} this
38909          */
38910         "slidehide" : true,
38911         /**
38912          * @event panelactivated
38913          * Fires when a panel is activated. 
38914          * @param {Roo.LayoutRegion} this
38915          * @param {Roo.ContentPanel} panel The activated panel
38916          */
38917         "panelactivated" : true,
38918         /**
38919          * @event resized
38920          * Fires when the user resizes this region. 
38921          * @param {Roo.LayoutRegion} this
38922          * @param {Number} newSize The new size (width for east/west, height for north/south)
38923          */
38924         "resized" : true
38925     };
38926     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38927     this.panels = new Roo.util.MixedCollection();
38928     this.panels.getKey = this.getPanelId.createDelegate(this);
38929     this.box = null;
38930     this.activePanel = null;
38931     // ensure listeners are added...
38932     
38933     if (config.listeners || config.events) {
38934         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38935             listeners : config.listeners || {},
38936             events : config.events || {}
38937         });
38938     }
38939     
38940     if(skipConfig !== true){
38941         this.applyConfig(config);
38942     }
38943 };
38944
38945 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38946 {
38947     getPanelId : function(p){
38948         return p.getId();
38949     },
38950     
38951     applyConfig : function(config){
38952         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38953         this.config = config;
38954         
38955     },
38956     
38957     /**
38958      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38959      * the width, for horizontal (north, south) the height.
38960      * @param {Number} newSize The new width or height
38961      */
38962     resizeTo : function(newSize){
38963         var el = this.el ? this.el :
38964                  (this.activePanel ? this.activePanel.getEl() : null);
38965         if(el){
38966             switch(this.position){
38967                 case "east":
38968                 case "west":
38969                     el.setWidth(newSize);
38970                     this.fireEvent("resized", this, newSize);
38971                 break;
38972                 case "north":
38973                 case "south":
38974                     el.setHeight(newSize);
38975                     this.fireEvent("resized", this, newSize);
38976                 break;                
38977             }
38978         }
38979     },
38980     
38981     getBox : function(){
38982         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38983     },
38984     
38985     getMargins : function(){
38986         return this.margins;
38987     },
38988     
38989     updateBox : function(box){
38990         this.box = box;
38991         var el = this.activePanel.getEl();
38992         el.dom.style.left = box.x + "px";
38993         el.dom.style.top = box.y + "px";
38994         this.activePanel.setSize(box.width, box.height);
38995     },
38996     
38997     /**
38998      * Returns the container element for this region.
38999      * @return {Roo.Element}
39000      */
39001     getEl : function(){
39002         return this.activePanel;
39003     },
39004     
39005     /**
39006      * Returns true if this region is currently visible.
39007      * @return {Boolean}
39008      */
39009     isVisible : function(){
39010         return this.activePanel ? true : false;
39011     },
39012     
39013     setActivePanel : function(panel){
39014         panel = this.getPanel(panel);
39015         if(this.activePanel && this.activePanel != panel){
39016             this.activePanel.setActiveState(false);
39017             this.activePanel.getEl().setLeftTop(-10000,-10000);
39018         }
39019         this.activePanel = panel;
39020         panel.setActiveState(true);
39021         if(this.box){
39022             panel.setSize(this.box.width, this.box.height);
39023         }
39024         this.fireEvent("panelactivated", this, panel);
39025         this.fireEvent("invalidated");
39026     },
39027     
39028     /**
39029      * Show the specified panel.
39030      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39031      * @return {Roo.ContentPanel} The shown panel or null
39032      */
39033     showPanel : function(panel){
39034         panel = this.getPanel(panel);
39035         if(panel){
39036             this.setActivePanel(panel);
39037         }
39038         return panel;
39039     },
39040     
39041     /**
39042      * Get the active panel for this region.
39043      * @return {Roo.ContentPanel} The active panel or null
39044      */
39045     getActivePanel : function(){
39046         return this.activePanel;
39047     },
39048     
39049     /**
39050      * Add the passed ContentPanel(s)
39051      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39052      * @return {Roo.ContentPanel} The panel added (if only one was added)
39053      */
39054     add : function(panel){
39055         if(arguments.length > 1){
39056             for(var i = 0, len = arguments.length; i < len; i++) {
39057                 this.add(arguments[i]);
39058             }
39059             return null;
39060         }
39061         if(this.hasPanel(panel)){
39062             this.showPanel(panel);
39063             return panel;
39064         }
39065         var el = panel.getEl();
39066         if(el.dom.parentNode != this.mgr.el.dom){
39067             this.mgr.el.dom.appendChild(el.dom);
39068         }
39069         if(panel.setRegion){
39070             panel.setRegion(this);
39071         }
39072         this.panels.add(panel);
39073         el.setStyle("position", "absolute");
39074         if(!panel.background){
39075             this.setActivePanel(panel);
39076             if(this.config.initialSize && this.panels.getCount()==1){
39077                 this.resizeTo(this.config.initialSize);
39078             }
39079         }
39080         this.fireEvent("paneladded", this, panel);
39081         return panel;
39082     },
39083     
39084     /**
39085      * Returns true if the panel is in this region.
39086      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39087      * @return {Boolean}
39088      */
39089     hasPanel : function(panel){
39090         if(typeof panel == "object"){ // must be panel obj
39091             panel = panel.getId();
39092         }
39093         return this.getPanel(panel) ? true : false;
39094     },
39095     
39096     /**
39097      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39098      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39099      * @param {Boolean} preservePanel Overrides the config preservePanel option
39100      * @return {Roo.ContentPanel} The panel that was removed
39101      */
39102     remove : function(panel, preservePanel){
39103         panel = this.getPanel(panel);
39104         if(!panel){
39105             return null;
39106         }
39107         var e = {};
39108         this.fireEvent("beforeremove", this, panel, e);
39109         if(e.cancel === true){
39110             return null;
39111         }
39112         var panelId = panel.getId();
39113         this.panels.removeKey(panelId);
39114         return panel;
39115     },
39116     
39117     /**
39118      * Returns the panel specified or null if it's not in this region.
39119      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39120      * @return {Roo.ContentPanel}
39121      */
39122     getPanel : function(id){
39123         if(typeof id == "object"){ // must be panel obj
39124             return id;
39125         }
39126         return this.panels.get(id);
39127     },
39128     
39129     /**
39130      * Returns this regions position (north/south/east/west/center).
39131      * @return {String} 
39132      */
39133     getPosition: function(){
39134         return this.position;    
39135     }
39136 });/*
39137  * Based on:
39138  * Ext JS Library 1.1.1
39139  * Copyright(c) 2006-2007, Ext JS, LLC.
39140  *
39141  * Originally Released Under LGPL - original licence link has changed is not relivant.
39142  *
39143  * Fork - LGPL
39144  * <script type="text/javascript">
39145  */
39146  
39147 /**
39148  * @class Roo.bootstrap.layout.Region
39149  * @extends Roo.bootstrap.layout.Basic
39150  * This class represents a region in a layout manager.
39151  
39152  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39153  * @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})
39154  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39155  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39156  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39157  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39158  * @cfg {String}    title           The title for the region (overrides panel titles)
39159  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39160  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39161  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39162  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39163  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39164  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39165  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39166  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39167  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39168  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39169
39170  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39171  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39172  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39173  * @cfg {Number}    width           For East/West panels
39174  * @cfg {Number}    height          For North/South panels
39175  * @cfg {Boolean}   split           To show the splitter
39176  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39177  * 
39178  * @cfg {string}   cls             Extra CSS classes to add to region
39179  * 
39180  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39181  * @cfg {string}   region  the region that it inhabits..
39182  *
39183
39184  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39185  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39186
39187  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39188  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39189  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39190  */
39191 Roo.bootstrap.layout.Region = function(config)
39192 {
39193     this.applyConfig(config);
39194
39195     var mgr = config.mgr;
39196     var pos = config.region;
39197     config.skipConfig = true;
39198     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39199     
39200     if (mgr.el) {
39201         this.onRender(mgr.el);   
39202     }
39203      
39204     this.visible = true;
39205     this.collapsed = false;
39206     this.unrendered_panels = [];
39207 };
39208
39209 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39210
39211     position: '', // set by wrapper (eg. north/south etc..)
39212     unrendered_panels : null,  // unrendered panels.
39213     
39214     tabPosition : false,
39215     
39216     mgr: false, // points to 'Border'
39217     
39218     
39219     createBody : function(){
39220         /** This region's body element 
39221         * @type Roo.Element */
39222         this.bodyEl = this.el.createChild({
39223                 tag: "div",
39224                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39225         });
39226     },
39227
39228     onRender: function(ctr, pos)
39229     {
39230         var dh = Roo.DomHelper;
39231         /** This region's container element 
39232         * @type Roo.Element */
39233         this.el = dh.append(ctr.dom, {
39234                 tag: "div",
39235                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39236             }, true);
39237         /** This region's title element 
39238         * @type Roo.Element */
39239     
39240         this.titleEl = dh.append(this.el.dom,  {
39241                 tag: "div",
39242                 unselectable: "on",
39243                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39244                 children:[
39245                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39246                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39247                 ]
39248             }, true);
39249         
39250         this.titleEl.enableDisplayMode();
39251         /** This region's title text element 
39252         * @type HTMLElement */
39253         this.titleTextEl = this.titleEl.dom.firstChild;
39254         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39255         /*
39256         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39257         this.closeBtn.enableDisplayMode();
39258         this.closeBtn.on("click", this.closeClicked, this);
39259         this.closeBtn.hide();
39260     */
39261         this.createBody(this.config);
39262         if(this.config.hideWhenEmpty){
39263             this.hide();
39264             this.on("paneladded", this.validateVisibility, this);
39265             this.on("panelremoved", this.validateVisibility, this);
39266         }
39267         if(this.autoScroll){
39268             this.bodyEl.setStyle("overflow", "auto");
39269         }else{
39270             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39271         }
39272         //if(c.titlebar !== false){
39273             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39274                 this.titleEl.hide();
39275             }else{
39276                 this.titleEl.show();
39277                 if(this.config.title){
39278                     this.titleTextEl.innerHTML = this.config.title;
39279                 }
39280             }
39281         //}
39282         if(this.config.collapsed){
39283             this.collapse(true);
39284         }
39285         if(this.config.hidden){
39286             this.hide();
39287         }
39288         
39289         if (this.unrendered_panels && this.unrendered_panels.length) {
39290             for (var i =0;i< this.unrendered_panels.length; i++) {
39291                 this.add(this.unrendered_panels[i]);
39292             }
39293             this.unrendered_panels = null;
39294             
39295         }
39296         
39297     },
39298     
39299     applyConfig : function(c)
39300     {
39301         /*
39302          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39303             var dh = Roo.DomHelper;
39304             if(c.titlebar !== false){
39305                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39306                 this.collapseBtn.on("click", this.collapse, this);
39307                 this.collapseBtn.enableDisplayMode();
39308                 /*
39309                 if(c.showPin === true || this.showPin){
39310                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39311                     this.stickBtn.enableDisplayMode();
39312                     this.stickBtn.on("click", this.expand, this);
39313                     this.stickBtn.hide();
39314                 }
39315                 
39316             }
39317             */
39318             /** This region's collapsed element
39319             * @type Roo.Element */
39320             /*
39321              *
39322             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39323                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39324             ]}, true);
39325             
39326             if(c.floatable !== false){
39327                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39328                this.collapsedEl.on("click", this.collapseClick, this);
39329             }
39330
39331             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39332                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39333                    id: "message", unselectable: "on", style:{"float":"left"}});
39334                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39335              }
39336             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39337             this.expandBtn.on("click", this.expand, this);
39338             
39339         }
39340         
39341         if(this.collapseBtn){
39342             this.collapseBtn.setVisible(c.collapsible == true);
39343         }
39344         
39345         this.cmargins = c.cmargins || this.cmargins ||
39346                          (this.position == "west" || this.position == "east" ?
39347                              {top: 0, left: 2, right:2, bottom: 0} :
39348                              {top: 2, left: 0, right:0, bottom: 2});
39349         */
39350         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39351         
39352         
39353         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39354         
39355         this.autoScroll = c.autoScroll || false;
39356         
39357         
39358        
39359         
39360         this.duration = c.duration || .30;
39361         this.slideDuration = c.slideDuration || .45;
39362         this.config = c;
39363        
39364     },
39365     /**
39366      * Returns true if this region is currently visible.
39367      * @return {Boolean}
39368      */
39369     isVisible : function(){
39370         return this.visible;
39371     },
39372
39373     /**
39374      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39375      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39376      */
39377     //setCollapsedTitle : function(title){
39378     //    title = title || "&#160;";
39379      //   if(this.collapsedTitleTextEl){
39380       //      this.collapsedTitleTextEl.innerHTML = title;
39381        // }
39382     //},
39383
39384     getBox : function(){
39385         var b;
39386       //  if(!this.collapsed){
39387             b = this.el.getBox(false, true);
39388        // }else{
39389           //  b = this.collapsedEl.getBox(false, true);
39390         //}
39391         return b;
39392     },
39393
39394     getMargins : function(){
39395         return this.margins;
39396         //return this.collapsed ? this.cmargins : this.margins;
39397     },
39398 /*
39399     highlight : function(){
39400         this.el.addClass("x-layout-panel-dragover");
39401     },
39402
39403     unhighlight : function(){
39404         this.el.removeClass("x-layout-panel-dragover");
39405     },
39406 */
39407     updateBox : function(box)
39408     {
39409         if (!this.bodyEl) {
39410             return; // not rendered yet..
39411         }
39412         
39413         this.box = box;
39414         if(!this.collapsed){
39415             this.el.dom.style.left = box.x + "px";
39416             this.el.dom.style.top = box.y + "px";
39417             this.updateBody(box.width, box.height);
39418         }else{
39419             this.collapsedEl.dom.style.left = box.x + "px";
39420             this.collapsedEl.dom.style.top = box.y + "px";
39421             this.collapsedEl.setSize(box.width, box.height);
39422         }
39423         if(this.tabs){
39424             this.tabs.autoSizeTabs();
39425         }
39426     },
39427
39428     updateBody : function(w, h)
39429     {
39430         if(w !== null){
39431             this.el.setWidth(w);
39432             w -= this.el.getBorderWidth("rl");
39433             if(this.config.adjustments){
39434                 w += this.config.adjustments[0];
39435             }
39436         }
39437         if(h !== null && h > 0){
39438             this.el.setHeight(h);
39439             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39440             h -= this.el.getBorderWidth("tb");
39441             if(this.config.adjustments){
39442                 h += this.config.adjustments[1];
39443             }
39444             this.bodyEl.setHeight(h);
39445             if(this.tabs){
39446                 h = this.tabs.syncHeight(h);
39447             }
39448         }
39449         if(this.panelSize){
39450             w = w !== null ? w : this.panelSize.width;
39451             h = h !== null ? h : this.panelSize.height;
39452         }
39453         if(this.activePanel){
39454             var el = this.activePanel.getEl();
39455             w = w !== null ? w : el.getWidth();
39456             h = h !== null ? h : el.getHeight();
39457             this.panelSize = {width: w, height: h};
39458             this.activePanel.setSize(w, h);
39459         }
39460         if(Roo.isIE && this.tabs){
39461             this.tabs.el.repaint();
39462         }
39463     },
39464
39465     /**
39466      * Returns the container element for this region.
39467      * @return {Roo.Element}
39468      */
39469     getEl : function(){
39470         return this.el;
39471     },
39472
39473     /**
39474      * Hides this region.
39475      */
39476     hide : function(){
39477         //if(!this.collapsed){
39478             this.el.dom.style.left = "-2000px";
39479             this.el.hide();
39480         //}else{
39481          //   this.collapsedEl.dom.style.left = "-2000px";
39482          //   this.collapsedEl.hide();
39483        // }
39484         this.visible = false;
39485         this.fireEvent("visibilitychange", this, false);
39486     },
39487
39488     /**
39489      * Shows this region if it was previously hidden.
39490      */
39491     show : function(){
39492         //if(!this.collapsed){
39493             this.el.show();
39494         //}else{
39495         //    this.collapsedEl.show();
39496        // }
39497         this.visible = true;
39498         this.fireEvent("visibilitychange", this, true);
39499     },
39500 /*
39501     closeClicked : function(){
39502         if(this.activePanel){
39503             this.remove(this.activePanel);
39504         }
39505     },
39506
39507     collapseClick : function(e){
39508         if(this.isSlid){
39509            e.stopPropagation();
39510            this.slideIn();
39511         }else{
39512            e.stopPropagation();
39513            this.slideOut();
39514         }
39515     },
39516 */
39517     /**
39518      * Collapses this region.
39519      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39520      */
39521     /*
39522     collapse : function(skipAnim, skipCheck = false){
39523         if(this.collapsed) {
39524             return;
39525         }
39526         
39527         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39528             
39529             this.collapsed = true;
39530             if(this.split){
39531                 this.split.el.hide();
39532             }
39533             if(this.config.animate && skipAnim !== true){
39534                 this.fireEvent("invalidated", this);
39535                 this.animateCollapse();
39536             }else{
39537                 this.el.setLocation(-20000,-20000);
39538                 this.el.hide();
39539                 this.collapsedEl.show();
39540                 this.fireEvent("collapsed", this);
39541                 this.fireEvent("invalidated", this);
39542             }
39543         }
39544         
39545     },
39546 */
39547     animateCollapse : function(){
39548         // overridden
39549     },
39550
39551     /**
39552      * Expands this region if it was previously collapsed.
39553      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39554      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39555      */
39556     /*
39557     expand : function(e, skipAnim){
39558         if(e) {
39559             e.stopPropagation();
39560         }
39561         if(!this.collapsed || this.el.hasActiveFx()) {
39562             return;
39563         }
39564         if(this.isSlid){
39565             this.afterSlideIn();
39566             skipAnim = true;
39567         }
39568         this.collapsed = false;
39569         if(this.config.animate && skipAnim !== true){
39570             this.animateExpand();
39571         }else{
39572             this.el.show();
39573             if(this.split){
39574                 this.split.el.show();
39575             }
39576             this.collapsedEl.setLocation(-2000,-2000);
39577             this.collapsedEl.hide();
39578             this.fireEvent("invalidated", this);
39579             this.fireEvent("expanded", this);
39580         }
39581     },
39582 */
39583     animateExpand : function(){
39584         // overridden
39585     },
39586
39587     initTabs : function()
39588     {
39589         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39590         
39591         var ts = new Roo.bootstrap.panel.Tabs({
39592             el: this.bodyEl.dom,
39593             region : this,
39594             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39595             disableTooltips: this.config.disableTabTips,
39596             toolbar : this.config.toolbar
39597         });
39598         
39599         if(this.config.hideTabs){
39600             ts.stripWrap.setDisplayed(false);
39601         }
39602         this.tabs = ts;
39603         ts.resizeTabs = this.config.resizeTabs === true;
39604         ts.minTabWidth = this.config.minTabWidth || 40;
39605         ts.maxTabWidth = this.config.maxTabWidth || 250;
39606         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39607         ts.monitorResize = false;
39608         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39609         ts.bodyEl.addClass('roo-layout-tabs-body');
39610         this.panels.each(this.initPanelAsTab, this);
39611     },
39612
39613     initPanelAsTab : function(panel){
39614         var ti = this.tabs.addTab(
39615             panel.getEl().id,
39616             panel.getTitle(),
39617             null,
39618             this.config.closeOnTab && panel.isClosable(),
39619             panel.tpl
39620         );
39621         if(panel.tabTip !== undefined){
39622             ti.setTooltip(panel.tabTip);
39623         }
39624         ti.on("activate", function(){
39625               this.setActivePanel(panel);
39626         }, this);
39627         
39628         if(this.config.closeOnTab){
39629             ti.on("beforeclose", function(t, e){
39630                 e.cancel = true;
39631                 this.remove(panel);
39632             }, this);
39633         }
39634         
39635         panel.tabItem = ti;
39636         
39637         return ti;
39638     },
39639
39640     updatePanelTitle : function(panel, title)
39641     {
39642         if(this.activePanel == panel){
39643             this.updateTitle(title);
39644         }
39645         if(this.tabs){
39646             var ti = this.tabs.getTab(panel.getEl().id);
39647             ti.setText(title);
39648             if(panel.tabTip !== undefined){
39649                 ti.setTooltip(panel.tabTip);
39650             }
39651         }
39652     },
39653
39654     updateTitle : function(title){
39655         if(this.titleTextEl && !this.config.title){
39656             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39657         }
39658     },
39659
39660     setActivePanel : function(panel)
39661     {
39662         panel = this.getPanel(panel);
39663         if(this.activePanel && this.activePanel != panel){
39664             if(this.activePanel.setActiveState(false) === false){
39665                 return;
39666             }
39667         }
39668         this.activePanel = panel;
39669         panel.setActiveState(true);
39670         if(this.panelSize){
39671             panel.setSize(this.panelSize.width, this.panelSize.height);
39672         }
39673         if(this.closeBtn){
39674             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39675         }
39676         this.updateTitle(panel.getTitle());
39677         if(this.tabs){
39678             this.fireEvent("invalidated", this);
39679         }
39680         this.fireEvent("panelactivated", this, panel);
39681     },
39682
39683     /**
39684      * Shows the specified panel.
39685      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39686      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39687      */
39688     showPanel : function(panel)
39689     {
39690         panel = this.getPanel(panel);
39691         if(panel){
39692             if(this.tabs){
39693                 var tab = this.tabs.getTab(panel.getEl().id);
39694                 if(tab.isHidden()){
39695                     this.tabs.unhideTab(tab.id);
39696                 }
39697                 tab.activate();
39698             }else{
39699                 this.setActivePanel(panel);
39700             }
39701         }
39702         return panel;
39703     },
39704
39705     /**
39706      * Get the active panel for this region.
39707      * @return {Roo.ContentPanel} The active panel or null
39708      */
39709     getActivePanel : function(){
39710         return this.activePanel;
39711     },
39712
39713     validateVisibility : function(){
39714         if(this.panels.getCount() < 1){
39715             this.updateTitle("&#160;");
39716             this.closeBtn.hide();
39717             this.hide();
39718         }else{
39719             if(!this.isVisible()){
39720                 this.show();
39721             }
39722         }
39723     },
39724
39725     /**
39726      * Adds the passed ContentPanel(s) to this region.
39727      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39728      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39729      */
39730     add : function(panel)
39731     {
39732         if(arguments.length > 1){
39733             for(var i = 0, len = arguments.length; i < len; i++) {
39734                 this.add(arguments[i]);
39735             }
39736             return null;
39737         }
39738         
39739         // if we have not been rendered yet, then we can not really do much of this..
39740         if (!this.bodyEl) {
39741             this.unrendered_panels.push(panel);
39742             return panel;
39743         }
39744         
39745         
39746         
39747         
39748         if(this.hasPanel(panel)){
39749             this.showPanel(panel);
39750             return panel;
39751         }
39752         panel.setRegion(this);
39753         this.panels.add(panel);
39754        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39755             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39756             // and hide them... ???
39757             this.bodyEl.dom.appendChild(panel.getEl().dom);
39758             if(panel.background !== true){
39759                 this.setActivePanel(panel);
39760             }
39761             this.fireEvent("paneladded", this, panel);
39762             return panel;
39763         }
39764         */
39765         if(!this.tabs){
39766             this.initTabs();
39767         }else{
39768             this.initPanelAsTab(panel);
39769         }
39770         
39771         
39772         if(panel.background !== true){
39773             this.tabs.activate(panel.getEl().id);
39774         }
39775         this.fireEvent("paneladded", this, panel);
39776         return panel;
39777     },
39778
39779     /**
39780      * Hides the tab for the specified panel.
39781      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39782      */
39783     hidePanel : function(panel){
39784         if(this.tabs && (panel = this.getPanel(panel))){
39785             this.tabs.hideTab(panel.getEl().id);
39786         }
39787     },
39788
39789     /**
39790      * Unhides the tab for a previously hidden panel.
39791      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39792      */
39793     unhidePanel : function(panel){
39794         if(this.tabs && (panel = this.getPanel(panel))){
39795             this.tabs.unhideTab(panel.getEl().id);
39796         }
39797     },
39798
39799     clearPanels : function(){
39800         while(this.panels.getCount() > 0){
39801              this.remove(this.panels.first());
39802         }
39803     },
39804
39805     /**
39806      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39807      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39808      * @param {Boolean} preservePanel Overrides the config preservePanel option
39809      * @return {Roo.ContentPanel} The panel that was removed
39810      */
39811     remove : function(panel, preservePanel)
39812     {
39813         panel = this.getPanel(panel);
39814         if(!panel){
39815             return null;
39816         }
39817         var e = {};
39818         this.fireEvent("beforeremove", this, panel, e);
39819         if(e.cancel === true){
39820             return null;
39821         }
39822         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39823         var panelId = panel.getId();
39824         this.panels.removeKey(panelId);
39825         if(preservePanel){
39826             document.body.appendChild(panel.getEl().dom);
39827         }
39828         if(this.tabs){
39829             this.tabs.removeTab(panel.getEl().id);
39830         }else if (!preservePanel){
39831             this.bodyEl.dom.removeChild(panel.getEl().dom);
39832         }
39833         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39834             var p = this.panels.first();
39835             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39836             tempEl.appendChild(p.getEl().dom);
39837             this.bodyEl.update("");
39838             this.bodyEl.dom.appendChild(p.getEl().dom);
39839             tempEl = null;
39840             this.updateTitle(p.getTitle());
39841             this.tabs = null;
39842             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39843             this.setActivePanel(p);
39844         }
39845         panel.setRegion(null);
39846         if(this.activePanel == panel){
39847             this.activePanel = null;
39848         }
39849         if(this.config.autoDestroy !== false && preservePanel !== true){
39850             try{panel.destroy();}catch(e){}
39851         }
39852         this.fireEvent("panelremoved", this, panel);
39853         return panel;
39854     },
39855
39856     /**
39857      * Returns the TabPanel component used by this region
39858      * @return {Roo.TabPanel}
39859      */
39860     getTabs : function(){
39861         return this.tabs;
39862     },
39863
39864     createTool : function(parentEl, className){
39865         var btn = Roo.DomHelper.append(parentEl, {
39866             tag: "div",
39867             cls: "x-layout-tools-button",
39868             children: [ {
39869                 tag: "div",
39870                 cls: "roo-layout-tools-button-inner " + className,
39871                 html: "&#160;"
39872             }]
39873         }, true);
39874         btn.addClassOnOver("roo-layout-tools-button-over");
39875         return btn;
39876     }
39877 });/*
39878  * Based on:
39879  * Ext JS Library 1.1.1
39880  * Copyright(c) 2006-2007, Ext JS, LLC.
39881  *
39882  * Originally Released Under LGPL - original licence link has changed is not relivant.
39883  *
39884  * Fork - LGPL
39885  * <script type="text/javascript">
39886  */
39887  
39888
39889
39890 /**
39891  * @class Roo.SplitLayoutRegion
39892  * @extends Roo.LayoutRegion
39893  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39894  */
39895 Roo.bootstrap.layout.Split = function(config){
39896     this.cursor = config.cursor;
39897     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39898 };
39899
39900 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39901 {
39902     splitTip : "Drag to resize.",
39903     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39904     useSplitTips : false,
39905
39906     applyConfig : function(config){
39907         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39908     },
39909     
39910     onRender : function(ctr,pos) {
39911         
39912         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39913         if(!this.config.split){
39914             return;
39915         }
39916         if(!this.split){
39917             
39918             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39919                             tag: "div",
39920                             id: this.el.id + "-split",
39921                             cls: "roo-layout-split roo-layout-split-"+this.position,
39922                             html: "&#160;"
39923             });
39924             /** The SplitBar for this region 
39925             * @type Roo.SplitBar */
39926             // does not exist yet...
39927             Roo.log([this.position, this.orientation]);
39928             
39929             this.split = new Roo.bootstrap.SplitBar({
39930                 dragElement : splitEl,
39931                 resizingElement: this.el,
39932                 orientation : this.orientation
39933             });
39934             
39935             this.split.on("moved", this.onSplitMove, this);
39936             this.split.useShim = this.config.useShim === true;
39937             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39938             if(this.useSplitTips){
39939                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39940             }
39941             //if(config.collapsible){
39942             //    this.split.el.on("dblclick", this.collapse,  this);
39943             //}
39944         }
39945         if(typeof this.config.minSize != "undefined"){
39946             this.split.minSize = this.config.minSize;
39947         }
39948         if(typeof this.config.maxSize != "undefined"){
39949             this.split.maxSize = this.config.maxSize;
39950         }
39951         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39952             this.hideSplitter();
39953         }
39954         
39955     },
39956
39957     getHMaxSize : function(){
39958          var cmax = this.config.maxSize || 10000;
39959          var center = this.mgr.getRegion("center");
39960          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39961     },
39962
39963     getVMaxSize : function(){
39964          var cmax = this.config.maxSize || 10000;
39965          var center = this.mgr.getRegion("center");
39966          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39967     },
39968
39969     onSplitMove : function(split, newSize){
39970         this.fireEvent("resized", this, newSize);
39971     },
39972     
39973     /** 
39974      * Returns the {@link Roo.SplitBar} for this region.
39975      * @return {Roo.SplitBar}
39976      */
39977     getSplitBar : function(){
39978         return this.split;
39979     },
39980     
39981     hide : function(){
39982         this.hideSplitter();
39983         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39984     },
39985
39986     hideSplitter : function(){
39987         if(this.split){
39988             this.split.el.setLocation(-2000,-2000);
39989             this.split.el.hide();
39990         }
39991     },
39992
39993     show : function(){
39994         if(this.split){
39995             this.split.el.show();
39996         }
39997         Roo.bootstrap.layout.Split.superclass.show.call(this);
39998     },
39999     
40000     beforeSlide: function(){
40001         if(Roo.isGecko){// firefox overflow auto bug workaround
40002             this.bodyEl.clip();
40003             if(this.tabs) {
40004                 this.tabs.bodyEl.clip();
40005             }
40006             if(this.activePanel){
40007                 this.activePanel.getEl().clip();
40008                 
40009                 if(this.activePanel.beforeSlide){
40010                     this.activePanel.beforeSlide();
40011                 }
40012             }
40013         }
40014     },
40015     
40016     afterSlide : function(){
40017         if(Roo.isGecko){// firefox overflow auto bug workaround
40018             this.bodyEl.unclip();
40019             if(this.tabs) {
40020                 this.tabs.bodyEl.unclip();
40021             }
40022             if(this.activePanel){
40023                 this.activePanel.getEl().unclip();
40024                 if(this.activePanel.afterSlide){
40025                     this.activePanel.afterSlide();
40026                 }
40027             }
40028         }
40029     },
40030
40031     initAutoHide : function(){
40032         if(this.autoHide !== false){
40033             if(!this.autoHideHd){
40034                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40035                 this.autoHideHd = {
40036                     "mouseout": function(e){
40037                         if(!e.within(this.el, true)){
40038                             st.delay(500);
40039                         }
40040                     },
40041                     "mouseover" : function(e){
40042                         st.cancel();
40043                     },
40044                     scope : this
40045                 };
40046             }
40047             this.el.on(this.autoHideHd);
40048         }
40049     },
40050
40051     clearAutoHide : function(){
40052         if(this.autoHide !== false){
40053             this.el.un("mouseout", this.autoHideHd.mouseout);
40054             this.el.un("mouseover", this.autoHideHd.mouseover);
40055         }
40056     },
40057
40058     clearMonitor : function(){
40059         Roo.get(document).un("click", this.slideInIf, this);
40060     },
40061
40062     // these names are backwards but not changed for compat
40063     slideOut : function(){
40064         if(this.isSlid || this.el.hasActiveFx()){
40065             return;
40066         }
40067         this.isSlid = true;
40068         if(this.collapseBtn){
40069             this.collapseBtn.hide();
40070         }
40071         this.closeBtnState = this.closeBtn.getStyle('display');
40072         this.closeBtn.hide();
40073         if(this.stickBtn){
40074             this.stickBtn.show();
40075         }
40076         this.el.show();
40077         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40078         this.beforeSlide();
40079         this.el.setStyle("z-index", 10001);
40080         this.el.slideIn(this.getSlideAnchor(), {
40081             callback: function(){
40082                 this.afterSlide();
40083                 this.initAutoHide();
40084                 Roo.get(document).on("click", this.slideInIf, this);
40085                 this.fireEvent("slideshow", this);
40086             },
40087             scope: this,
40088             block: true
40089         });
40090     },
40091
40092     afterSlideIn : function(){
40093         this.clearAutoHide();
40094         this.isSlid = false;
40095         this.clearMonitor();
40096         this.el.setStyle("z-index", "");
40097         if(this.collapseBtn){
40098             this.collapseBtn.show();
40099         }
40100         this.closeBtn.setStyle('display', this.closeBtnState);
40101         if(this.stickBtn){
40102             this.stickBtn.hide();
40103         }
40104         this.fireEvent("slidehide", this);
40105     },
40106
40107     slideIn : function(cb){
40108         if(!this.isSlid || this.el.hasActiveFx()){
40109             Roo.callback(cb);
40110             return;
40111         }
40112         this.isSlid = false;
40113         this.beforeSlide();
40114         this.el.slideOut(this.getSlideAnchor(), {
40115             callback: function(){
40116                 this.el.setLeftTop(-10000, -10000);
40117                 this.afterSlide();
40118                 this.afterSlideIn();
40119                 Roo.callback(cb);
40120             },
40121             scope: this,
40122             block: true
40123         });
40124     },
40125     
40126     slideInIf : function(e){
40127         if(!e.within(this.el)){
40128             this.slideIn();
40129         }
40130     },
40131
40132     animateCollapse : function(){
40133         this.beforeSlide();
40134         this.el.setStyle("z-index", 20000);
40135         var anchor = this.getSlideAnchor();
40136         this.el.slideOut(anchor, {
40137             callback : function(){
40138                 this.el.setStyle("z-index", "");
40139                 this.collapsedEl.slideIn(anchor, {duration:.3});
40140                 this.afterSlide();
40141                 this.el.setLocation(-10000,-10000);
40142                 this.el.hide();
40143                 this.fireEvent("collapsed", this);
40144             },
40145             scope: this,
40146             block: true
40147         });
40148     },
40149
40150     animateExpand : function(){
40151         this.beforeSlide();
40152         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40153         this.el.setStyle("z-index", 20000);
40154         this.collapsedEl.hide({
40155             duration:.1
40156         });
40157         this.el.slideIn(this.getSlideAnchor(), {
40158             callback : function(){
40159                 this.el.setStyle("z-index", "");
40160                 this.afterSlide();
40161                 if(this.split){
40162                     this.split.el.show();
40163                 }
40164                 this.fireEvent("invalidated", this);
40165                 this.fireEvent("expanded", this);
40166             },
40167             scope: this,
40168             block: true
40169         });
40170     },
40171
40172     anchors : {
40173         "west" : "left",
40174         "east" : "right",
40175         "north" : "top",
40176         "south" : "bottom"
40177     },
40178
40179     sanchors : {
40180         "west" : "l",
40181         "east" : "r",
40182         "north" : "t",
40183         "south" : "b"
40184     },
40185
40186     canchors : {
40187         "west" : "tl-tr",
40188         "east" : "tr-tl",
40189         "north" : "tl-bl",
40190         "south" : "bl-tl"
40191     },
40192
40193     getAnchor : function(){
40194         return this.anchors[this.position];
40195     },
40196
40197     getCollapseAnchor : function(){
40198         return this.canchors[this.position];
40199     },
40200
40201     getSlideAnchor : function(){
40202         return this.sanchors[this.position];
40203     },
40204
40205     getAlignAdj : function(){
40206         var cm = this.cmargins;
40207         switch(this.position){
40208             case "west":
40209                 return [0, 0];
40210             break;
40211             case "east":
40212                 return [0, 0];
40213             break;
40214             case "north":
40215                 return [0, 0];
40216             break;
40217             case "south":
40218                 return [0, 0];
40219             break;
40220         }
40221     },
40222
40223     getExpandAdj : function(){
40224         var c = this.collapsedEl, cm = this.cmargins;
40225         switch(this.position){
40226             case "west":
40227                 return [-(cm.right+c.getWidth()+cm.left), 0];
40228             break;
40229             case "east":
40230                 return [cm.right+c.getWidth()+cm.left, 0];
40231             break;
40232             case "north":
40233                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40234             break;
40235             case "south":
40236                 return [0, cm.top+cm.bottom+c.getHeight()];
40237             break;
40238         }
40239     }
40240 });/*
40241  * Based on:
40242  * Ext JS Library 1.1.1
40243  * Copyright(c) 2006-2007, Ext JS, LLC.
40244  *
40245  * Originally Released Under LGPL - original licence link has changed is not relivant.
40246  *
40247  * Fork - LGPL
40248  * <script type="text/javascript">
40249  */
40250 /*
40251  * These classes are private internal classes
40252  */
40253 Roo.bootstrap.layout.Center = function(config){
40254     config.region = "center";
40255     Roo.bootstrap.layout.Region.call(this, config);
40256     this.visible = true;
40257     this.minWidth = config.minWidth || 20;
40258     this.minHeight = config.minHeight || 20;
40259 };
40260
40261 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40262     hide : function(){
40263         // center panel can't be hidden
40264     },
40265     
40266     show : function(){
40267         // center panel can't be hidden
40268     },
40269     
40270     getMinWidth: function(){
40271         return this.minWidth;
40272     },
40273     
40274     getMinHeight: function(){
40275         return this.minHeight;
40276     }
40277 });
40278
40279
40280
40281
40282  
40283
40284
40285
40286
40287
40288
40289 Roo.bootstrap.layout.North = function(config)
40290 {
40291     config.region = 'north';
40292     config.cursor = 'n-resize';
40293     
40294     Roo.bootstrap.layout.Split.call(this, config);
40295     
40296     
40297     if(this.split){
40298         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40299         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40300         this.split.el.addClass("roo-layout-split-v");
40301     }
40302     //var size = config.initialSize || config.height;
40303     //if(this.el && typeof size != "undefined"){
40304     //    this.el.setHeight(size);
40305     //}
40306 };
40307 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40308 {
40309     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40310      
40311      
40312     onRender : function(ctr, pos)
40313     {
40314         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40315         var size = this.config.initialSize || this.config.height;
40316         if(this.el && typeof size != "undefined"){
40317             this.el.setHeight(size);
40318         }
40319     
40320     },
40321     
40322     getBox : function(){
40323         if(this.collapsed){
40324             return this.collapsedEl.getBox();
40325         }
40326         var box = this.el.getBox();
40327         if(this.split){
40328             box.height += this.split.el.getHeight();
40329         }
40330         return box;
40331     },
40332     
40333     updateBox : function(box){
40334         if(this.split && !this.collapsed){
40335             box.height -= this.split.el.getHeight();
40336             this.split.el.setLeft(box.x);
40337             this.split.el.setTop(box.y+box.height);
40338             this.split.el.setWidth(box.width);
40339         }
40340         if(this.collapsed){
40341             this.updateBody(box.width, null);
40342         }
40343         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40344     }
40345 });
40346
40347
40348
40349
40350
40351 Roo.bootstrap.layout.South = function(config){
40352     config.region = 'south';
40353     config.cursor = 's-resize';
40354     Roo.bootstrap.layout.Split.call(this, config);
40355     if(this.split){
40356         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40357         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40358         this.split.el.addClass("roo-layout-split-v");
40359     }
40360     
40361 };
40362
40363 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40364     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40365     
40366     onRender : function(ctr, pos)
40367     {
40368         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40369         var size = this.config.initialSize || this.config.height;
40370         if(this.el && typeof size != "undefined"){
40371             this.el.setHeight(size);
40372         }
40373     
40374     },
40375     
40376     getBox : function(){
40377         if(this.collapsed){
40378             return this.collapsedEl.getBox();
40379         }
40380         var box = this.el.getBox();
40381         if(this.split){
40382             var sh = this.split.el.getHeight();
40383             box.height += sh;
40384             box.y -= sh;
40385         }
40386         return box;
40387     },
40388     
40389     updateBox : function(box){
40390         if(this.split && !this.collapsed){
40391             var sh = this.split.el.getHeight();
40392             box.height -= sh;
40393             box.y += sh;
40394             this.split.el.setLeft(box.x);
40395             this.split.el.setTop(box.y-sh);
40396             this.split.el.setWidth(box.width);
40397         }
40398         if(this.collapsed){
40399             this.updateBody(box.width, null);
40400         }
40401         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40402     }
40403 });
40404
40405 Roo.bootstrap.layout.East = function(config){
40406     config.region = "east";
40407     config.cursor = "e-resize";
40408     Roo.bootstrap.layout.Split.call(this, config);
40409     if(this.split){
40410         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40411         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40412         this.split.el.addClass("roo-layout-split-h");
40413     }
40414     
40415 };
40416 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40417     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40418     
40419     onRender : function(ctr, pos)
40420     {
40421         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40422         var size = this.config.initialSize || this.config.width;
40423         if(this.el && typeof size != "undefined"){
40424             this.el.setWidth(size);
40425         }
40426     
40427     },
40428     
40429     getBox : function(){
40430         if(this.collapsed){
40431             return this.collapsedEl.getBox();
40432         }
40433         var box = this.el.getBox();
40434         if(this.split){
40435             var sw = this.split.el.getWidth();
40436             box.width += sw;
40437             box.x -= sw;
40438         }
40439         return box;
40440     },
40441
40442     updateBox : function(box){
40443         if(this.split && !this.collapsed){
40444             var sw = this.split.el.getWidth();
40445             box.width -= sw;
40446             this.split.el.setLeft(box.x);
40447             this.split.el.setTop(box.y);
40448             this.split.el.setHeight(box.height);
40449             box.x += sw;
40450         }
40451         if(this.collapsed){
40452             this.updateBody(null, box.height);
40453         }
40454         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40455     }
40456 });
40457
40458 Roo.bootstrap.layout.West = function(config){
40459     config.region = "west";
40460     config.cursor = "w-resize";
40461     
40462     Roo.bootstrap.layout.Split.call(this, config);
40463     if(this.split){
40464         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40465         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40466         this.split.el.addClass("roo-layout-split-h");
40467     }
40468     
40469 };
40470 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40471     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40472     
40473     onRender: function(ctr, pos)
40474     {
40475         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40476         var size = this.config.initialSize || this.config.width;
40477         if(typeof size != "undefined"){
40478             this.el.setWidth(size);
40479         }
40480     },
40481     
40482     getBox : function(){
40483         if(this.collapsed){
40484             return this.collapsedEl.getBox();
40485         }
40486         var box = this.el.getBox();
40487         if (box.width == 0) {
40488             box.width = this.config.width; // kludge?
40489         }
40490         if(this.split){
40491             box.width += this.split.el.getWidth();
40492         }
40493         return box;
40494     },
40495     
40496     updateBox : function(box){
40497         if(this.split && !this.collapsed){
40498             var sw = this.split.el.getWidth();
40499             box.width -= sw;
40500             this.split.el.setLeft(box.x+box.width);
40501             this.split.el.setTop(box.y);
40502             this.split.el.setHeight(box.height);
40503         }
40504         if(this.collapsed){
40505             this.updateBody(null, box.height);
40506         }
40507         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40508     }
40509 });Roo.namespace("Roo.bootstrap.panel");/*
40510  * Based on:
40511  * Ext JS Library 1.1.1
40512  * Copyright(c) 2006-2007, Ext JS, LLC.
40513  *
40514  * Originally Released Under LGPL - original licence link has changed is not relivant.
40515  *
40516  * Fork - LGPL
40517  * <script type="text/javascript">
40518  */
40519 /**
40520  * @class Roo.ContentPanel
40521  * @extends Roo.util.Observable
40522  * A basic ContentPanel element.
40523  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40524  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40525  * @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
40526  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40527  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40528  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40529  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40530  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40531  * @cfg {String} title          The title for this panel
40532  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40533  * @cfg {String} url            Calls {@link #setUrl} with this value
40534  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40535  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40536  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40537  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40538  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40539  * @cfg {Boolean} badges render the badges
40540  * @cfg {String} cls  extra classes to use  
40541  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40542
40543  * @constructor
40544  * Create a new ContentPanel.
40545  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40546  * @param {String/Object} config A string to set only the title or a config object
40547  * @param {String} content (optional) Set the HTML content for this panel
40548  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40549  */
40550 Roo.bootstrap.panel.Content = function( config){
40551     
40552     this.tpl = config.tpl || false;
40553     
40554     var el = config.el;
40555     var content = config.content;
40556
40557     if(config.autoCreate){ // xtype is available if this is called from factory
40558         el = Roo.id();
40559     }
40560     this.el = Roo.get(el);
40561     if(!this.el && config && config.autoCreate){
40562         if(typeof config.autoCreate == "object"){
40563             if(!config.autoCreate.id){
40564                 config.autoCreate.id = config.id||el;
40565             }
40566             this.el = Roo.DomHelper.append(document.body,
40567                         config.autoCreate, true);
40568         }else{
40569             var elcfg =  {
40570                 tag: "div",
40571                 cls: (config.cls || '') +
40572                     (config.background ? ' bg-' + config.background : '') +
40573                     " roo-layout-inactive-content",
40574                 id: config.id||el
40575             };
40576             if (config.iframe) {
40577                 elcfg.cn = [
40578                     {
40579                         tag : 'iframe',
40580                         style : 'border: 0px',
40581                         src : 'about:blank'
40582                     }
40583                 ];
40584             }
40585               
40586             if (config.html) {
40587                 elcfg.html = config.html;
40588                 
40589             }
40590                         
40591             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40592             if (config.iframe) {
40593                 this.iframeEl = this.el.select('iframe',true).first();
40594             }
40595             
40596         }
40597     } 
40598     this.closable = false;
40599     this.loaded = false;
40600     this.active = false;
40601    
40602       
40603     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40604         
40605         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40606         
40607         this.wrapEl = this.el; //this.el.wrap();
40608         var ti = [];
40609         if (config.toolbar.items) {
40610             ti = config.toolbar.items ;
40611             delete config.toolbar.items ;
40612         }
40613         
40614         var nitems = [];
40615         this.toolbar.render(this.wrapEl, 'before');
40616         for(var i =0;i < ti.length;i++) {
40617           //  Roo.log(['add child', items[i]]);
40618             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40619         }
40620         this.toolbar.items = nitems;
40621         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40622         delete config.toolbar;
40623         
40624     }
40625     /*
40626     // xtype created footer. - not sure if will work as we normally have to render first..
40627     if (this.footer && !this.footer.el && this.footer.xtype) {
40628         if (!this.wrapEl) {
40629             this.wrapEl = this.el.wrap();
40630         }
40631     
40632         this.footer.container = this.wrapEl.createChild();
40633          
40634         this.footer = Roo.factory(this.footer, Roo);
40635         
40636     }
40637     */
40638     
40639      if(typeof config == "string"){
40640         this.title = config;
40641     }else{
40642         Roo.apply(this, config);
40643     }
40644     
40645     if(this.resizeEl){
40646         this.resizeEl = Roo.get(this.resizeEl, true);
40647     }else{
40648         this.resizeEl = this.el;
40649     }
40650     // handle view.xtype
40651     
40652  
40653     
40654     
40655     this.addEvents({
40656         /**
40657          * @event activate
40658          * Fires when this panel is activated. 
40659          * @param {Roo.ContentPanel} this
40660          */
40661         "activate" : true,
40662         /**
40663          * @event deactivate
40664          * Fires when this panel is activated. 
40665          * @param {Roo.ContentPanel} this
40666          */
40667         "deactivate" : true,
40668
40669         /**
40670          * @event resize
40671          * Fires when this panel is resized if fitToFrame is true.
40672          * @param {Roo.ContentPanel} this
40673          * @param {Number} width The width after any component adjustments
40674          * @param {Number} height The height after any component adjustments
40675          */
40676         "resize" : true,
40677         
40678          /**
40679          * @event render
40680          * Fires when this tab is created
40681          * @param {Roo.ContentPanel} this
40682          */
40683         "render" : true,
40684         
40685           /**
40686          * @event scroll
40687          * Fires when this content is scrolled
40688          * @param {Roo.ContentPanel} this
40689          * @param {Event} scrollEvent
40690          */
40691         "scroll" : true
40692         
40693         
40694         
40695     });
40696     
40697
40698     
40699     
40700     if(this.autoScroll && !this.iframe){
40701         this.resizeEl.setStyle("overflow", "auto");
40702         this.resizeEl.on('scroll', this.onScroll, this);
40703     } else {
40704         // fix randome scrolling
40705         //this.el.on('scroll', function() {
40706         //    Roo.log('fix random scolling');
40707         //    this.scrollTo('top',0); 
40708         //});
40709     }
40710     content = content || this.content;
40711     if(content){
40712         this.setContent(content);
40713     }
40714     if(config && config.url){
40715         this.setUrl(this.url, this.params, this.loadOnce);
40716     }
40717     
40718     
40719     
40720     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40721     
40722     if (this.view && typeof(this.view.xtype) != 'undefined') {
40723         this.view.el = this.el.appendChild(document.createElement("div"));
40724         this.view = Roo.factory(this.view); 
40725         this.view.render  &&  this.view.render(false, '');  
40726     }
40727     
40728     
40729     this.fireEvent('render', this);
40730 };
40731
40732 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40733     
40734     cls : '',
40735     background : '',
40736     
40737     tabTip : '',
40738     
40739     iframe : false,
40740     iframeEl : false,
40741     
40742     /* Resize Element - use this to work out scroll etc. */
40743     resizeEl : false,
40744     
40745     setRegion : function(region){
40746         this.region = region;
40747         this.setActiveClass(region && !this.background);
40748     },
40749     
40750     
40751     setActiveClass: function(state)
40752     {
40753         if(state){
40754            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40755            this.el.setStyle('position','relative');
40756         }else{
40757            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40758            this.el.setStyle('position', 'absolute');
40759         } 
40760     },
40761     
40762     /**
40763      * Returns the toolbar for this Panel if one was configured. 
40764      * @return {Roo.Toolbar} 
40765      */
40766     getToolbar : function(){
40767         return this.toolbar;
40768     },
40769     
40770     setActiveState : function(active)
40771     {
40772         this.active = active;
40773         this.setActiveClass(active);
40774         if(!active){
40775             if(this.fireEvent("deactivate", this) === false){
40776                 return false;
40777             }
40778             return true;
40779         }
40780         this.fireEvent("activate", this);
40781         return true;
40782     },
40783     /**
40784      * Updates this panel's element (not for iframe)
40785      * @param {String} content The new content
40786      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40787     */
40788     setContent : function(content, loadScripts){
40789         if (this.iframe) {
40790             return;
40791         }
40792         
40793         this.el.update(content, loadScripts);
40794     },
40795
40796     ignoreResize : function(w, h){
40797         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40798             return true;
40799         }else{
40800             this.lastSize = {width: w, height: h};
40801             return false;
40802         }
40803     },
40804     /**
40805      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40806      * @return {Roo.UpdateManager} The UpdateManager
40807      */
40808     getUpdateManager : function(){
40809         if (this.iframe) {
40810             return false;
40811         }
40812         return this.el.getUpdateManager();
40813     },
40814      /**
40815      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40816      * Does not work with IFRAME contents
40817      * @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:
40818 <pre><code>
40819 panel.load({
40820     url: "your-url.php",
40821     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40822     callback: yourFunction,
40823     scope: yourObject, //(optional scope)
40824     discardUrl: false,
40825     nocache: false,
40826     text: "Loading...",
40827     timeout: 30,
40828     scripts: false
40829 });
40830 </code></pre>
40831      
40832      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40833      * 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.
40834      * @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}
40835      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40836      * @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.
40837      * @return {Roo.ContentPanel} this
40838      */
40839     load : function(){
40840         
40841         if (this.iframe) {
40842             return this;
40843         }
40844         
40845         var um = this.el.getUpdateManager();
40846         um.update.apply(um, arguments);
40847         return this;
40848     },
40849
40850
40851     /**
40852      * 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.
40853      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40854      * @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)
40855      * @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)
40856      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40857      */
40858     setUrl : function(url, params, loadOnce){
40859         if (this.iframe) {
40860             this.iframeEl.dom.src = url;
40861             return false;
40862         }
40863         
40864         if(this.refreshDelegate){
40865             this.removeListener("activate", this.refreshDelegate);
40866         }
40867         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40868         this.on("activate", this.refreshDelegate);
40869         return this.el.getUpdateManager();
40870     },
40871     
40872     _handleRefresh : function(url, params, loadOnce){
40873         if(!loadOnce || !this.loaded){
40874             var updater = this.el.getUpdateManager();
40875             updater.update(url, params, this._setLoaded.createDelegate(this));
40876         }
40877     },
40878     
40879     _setLoaded : function(){
40880         this.loaded = true;
40881     }, 
40882     
40883     /**
40884      * Returns this panel's id
40885      * @return {String} 
40886      */
40887     getId : function(){
40888         return this.el.id;
40889     },
40890     
40891     /** 
40892      * Returns this panel's element - used by regiosn to add.
40893      * @return {Roo.Element} 
40894      */
40895     getEl : function(){
40896         return this.wrapEl || this.el;
40897     },
40898     
40899    
40900     
40901     adjustForComponents : function(width, height)
40902     {
40903         //Roo.log('adjustForComponents ');
40904         if(this.resizeEl != this.el){
40905             width -= this.el.getFrameWidth('lr');
40906             height -= this.el.getFrameWidth('tb');
40907         }
40908         if(this.toolbar){
40909             var te = this.toolbar.getEl();
40910             te.setWidth(width);
40911             height -= te.getHeight();
40912         }
40913         if(this.footer){
40914             var te = this.footer.getEl();
40915             te.setWidth(width);
40916             height -= te.getHeight();
40917         }
40918         
40919         
40920         if(this.adjustments){
40921             width += this.adjustments[0];
40922             height += this.adjustments[1];
40923         }
40924         return {"width": width, "height": height};
40925     },
40926     
40927     setSize : function(width, height){
40928         if(this.fitToFrame && !this.ignoreResize(width, height)){
40929             if(this.fitContainer && this.resizeEl != this.el){
40930                 this.el.setSize(width, height);
40931             }
40932             var size = this.adjustForComponents(width, height);
40933             if (this.iframe) {
40934                 this.iframeEl.setSize(width,height);
40935             }
40936             
40937             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40938             this.fireEvent('resize', this, size.width, size.height);
40939             
40940             
40941         }
40942     },
40943     
40944     /**
40945      * Returns this panel's title
40946      * @return {String} 
40947      */
40948     getTitle : function(){
40949         
40950         if (typeof(this.title) != 'object') {
40951             return this.title;
40952         }
40953         
40954         var t = '';
40955         for (var k in this.title) {
40956             if (!this.title.hasOwnProperty(k)) {
40957                 continue;
40958             }
40959             
40960             if (k.indexOf('-') >= 0) {
40961                 var s = k.split('-');
40962                 for (var i = 0; i<s.length; i++) {
40963                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40964                 }
40965             } else {
40966                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40967             }
40968         }
40969         return t;
40970     },
40971     
40972     /**
40973      * Set this panel's title
40974      * @param {String} title
40975      */
40976     setTitle : function(title){
40977         this.title = title;
40978         if(this.region){
40979             this.region.updatePanelTitle(this, title);
40980         }
40981     },
40982     
40983     /**
40984      * Returns true is this panel was configured to be closable
40985      * @return {Boolean} 
40986      */
40987     isClosable : function(){
40988         return this.closable;
40989     },
40990     
40991     beforeSlide : function(){
40992         this.el.clip();
40993         this.resizeEl.clip();
40994     },
40995     
40996     afterSlide : function(){
40997         this.el.unclip();
40998         this.resizeEl.unclip();
40999     },
41000     
41001     /**
41002      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41003      *   Will fail silently if the {@link #setUrl} method has not been called.
41004      *   This does not activate the panel, just updates its content.
41005      */
41006     refresh : function(){
41007         if(this.refreshDelegate){
41008            this.loaded = false;
41009            this.refreshDelegate();
41010         }
41011     },
41012     
41013     /**
41014      * Destroys this panel
41015      */
41016     destroy : function(){
41017         this.el.removeAllListeners();
41018         var tempEl = document.createElement("span");
41019         tempEl.appendChild(this.el.dom);
41020         tempEl.innerHTML = "";
41021         this.el.remove();
41022         this.el = null;
41023     },
41024     
41025     /**
41026      * form - if the content panel contains a form - this is a reference to it.
41027      * @type {Roo.form.Form}
41028      */
41029     form : false,
41030     /**
41031      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41032      *    This contains a reference to it.
41033      * @type {Roo.View}
41034      */
41035     view : false,
41036     
41037       /**
41038      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41039      * <pre><code>
41040
41041 layout.addxtype({
41042        xtype : 'Form',
41043        items: [ .... ]
41044    }
41045 );
41046
41047 </code></pre>
41048      * @param {Object} cfg Xtype definition of item to add.
41049      */
41050     
41051     
41052     getChildContainer: function () {
41053         return this.getEl();
41054     },
41055     
41056     
41057     onScroll : function(e)
41058     {
41059         this.fireEvent('scroll', this, e);
41060     }
41061     
41062     
41063     /*
41064         var  ret = new Roo.factory(cfg);
41065         return ret;
41066         
41067         
41068         // add form..
41069         if (cfg.xtype.match(/^Form$/)) {
41070             
41071             var el;
41072             //if (this.footer) {
41073             //    el = this.footer.container.insertSibling(false, 'before');
41074             //} else {
41075                 el = this.el.createChild();
41076             //}
41077
41078             this.form = new  Roo.form.Form(cfg);
41079             
41080             
41081             if ( this.form.allItems.length) {
41082                 this.form.render(el.dom);
41083             }
41084             return this.form;
41085         }
41086         // should only have one of theses..
41087         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41088             // views.. should not be just added - used named prop 'view''
41089             
41090             cfg.el = this.el.appendChild(document.createElement("div"));
41091             // factory?
41092             
41093             var ret = new Roo.factory(cfg);
41094              
41095              ret.render && ret.render(false, ''); // render blank..
41096             this.view = ret;
41097             return ret;
41098         }
41099         return false;
41100     }
41101     \*/
41102 });
41103  
41104 /**
41105  * @class Roo.bootstrap.panel.Grid
41106  * @extends Roo.bootstrap.panel.Content
41107  * @constructor
41108  * Create a new GridPanel.
41109  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41110  * @param {Object} config A the config object
41111   
41112  */
41113
41114
41115
41116 Roo.bootstrap.panel.Grid = function(config)
41117 {
41118     
41119       
41120     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41121         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41122
41123     config.el = this.wrapper;
41124     //this.el = this.wrapper;
41125     
41126       if (config.container) {
41127         // ctor'ed from a Border/panel.grid
41128         
41129         
41130         this.wrapper.setStyle("overflow", "hidden");
41131         this.wrapper.addClass('roo-grid-container');
41132
41133     }
41134     
41135     
41136     if(config.toolbar){
41137         var tool_el = this.wrapper.createChild();    
41138         this.toolbar = Roo.factory(config.toolbar);
41139         var ti = [];
41140         if (config.toolbar.items) {
41141             ti = config.toolbar.items ;
41142             delete config.toolbar.items ;
41143         }
41144         
41145         var nitems = [];
41146         this.toolbar.render(tool_el);
41147         for(var i =0;i < ti.length;i++) {
41148           //  Roo.log(['add child', items[i]]);
41149             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41150         }
41151         this.toolbar.items = nitems;
41152         
41153         delete config.toolbar;
41154     }
41155     
41156     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41157     config.grid.scrollBody = true;;
41158     config.grid.monitorWindowResize = false; // turn off autosizing
41159     config.grid.autoHeight = false;
41160     config.grid.autoWidth = false;
41161     
41162     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41163     
41164     if (config.background) {
41165         // render grid on panel activation (if panel background)
41166         this.on('activate', function(gp) {
41167             if (!gp.grid.rendered) {
41168                 gp.grid.render(this.wrapper);
41169                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41170             }
41171         });
41172             
41173     } else {
41174         this.grid.render(this.wrapper);
41175         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41176
41177     }
41178     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41179     // ??? needed ??? config.el = this.wrapper;
41180     
41181     
41182     
41183   
41184     // xtype created footer. - not sure if will work as we normally have to render first..
41185     if (this.footer && !this.footer.el && this.footer.xtype) {
41186         
41187         var ctr = this.grid.getView().getFooterPanel(true);
41188         this.footer.dataSource = this.grid.dataSource;
41189         this.footer = Roo.factory(this.footer, Roo);
41190         this.footer.render(ctr);
41191         
41192     }
41193     
41194     
41195     
41196     
41197      
41198 };
41199
41200 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41201     getId : function(){
41202         return this.grid.id;
41203     },
41204     
41205     /**
41206      * Returns the grid for this panel
41207      * @return {Roo.bootstrap.Table} 
41208      */
41209     getGrid : function(){
41210         return this.grid;    
41211     },
41212     
41213     setSize : function(width, height){
41214         if(!this.ignoreResize(width, height)){
41215             var grid = this.grid;
41216             var size = this.adjustForComponents(width, height);
41217             // tfoot is not a footer?
41218           
41219             
41220             var gridel = grid.getGridEl();
41221             gridel.setSize(size.width, size.height);
41222             
41223             var tbd = grid.getGridEl().select('tbody', true).first();
41224             var thd = grid.getGridEl().select('thead',true).first();
41225             var tbf= grid.getGridEl().select('tfoot', true).first();
41226
41227             if (tbf) {
41228                 size.height -= tbf.getHeight();
41229             }
41230             if (thd) {
41231                 size.height -= thd.getHeight();
41232             }
41233             
41234             tbd.setSize(size.width, size.height );
41235             // this is for the account management tab -seems to work there.
41236             var thd = grid.getGridEl().select('thead',true).first();
41237             //if (tbd) {
41238             //    tbd.setSize(size.width, size.height - thd.getHeight());
41239             //}
41240              
41241             grid.autoSize();
41242         }
41243     },
41244      
41245     
41246     
41247     beforeSlide : function(){
41248         this.grid.getView().scroller.clip();
41249     },
41250     
41251     afterSlide : function(){
41252         this.grid.getView().scroller.unclip();
41253     },
41254     
41255     destroy : function(){
41256         this.grid.destroy();
41257         delete this.grid;
41258         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41259     }
41260 });
41261
41262 /**
41263  * @class Roo.bootstrap.panel.Nest
41264  * @extends Roo.bootstrap.panel.Content
41265  * @constructor
41266  * Create a new Panel, that can contain a layout.Border.
41267  * 
41268  * 
41269  * @param {Roo.BorderLayout} layout The layout for this panel
41270  * @param {String/Object} config A string to set only the title or a config object
41271  */
41272 Roo.bootstrap.panel.Nest = function(config)
41273 {
41274     // construct with only one argument..
41275     /* FIXME - implement nicer consturctors
41276     if (layout.layout) {
41277         config = layout;
41278         layout = config.layout;
41279         delete config.layout;
41280     }
41281     if (layout.xtype && !layout.getEl) {
41282         // then layout needs constructing..
41283         layout = Roo.factory(layout, Roo);
41284     }
41285     */
41286     
41287     config.el =  config.layout.getEl();
41288     
41289     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41290     
41291     config.layout.monitorWindowResize = false; // turn off autosizing
41292     this.layout = config.layout;
41293     this.layout.getEl().addClass("roo-layout-nested-layout");
41294     this.layout.parent = this;
41295     
41296     
41297     
41298     
41299 };
41300
41301 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41302
41303     setSize : function(width, height){
41304         if(!this.ignoreResize(width, height)){
41305             var size = this.adjustForComponents(width, height);
41306             var el = this.layout.getEl();
41307             if (size.height < 1) {
41308                 el.setWidth(size.width);   
41309             } else {
41310                 el.setSize(size.width, size.height);
41311             }
41312             var touch = el.dom.offsetWidth;
41313             this.layout.layout();
41314             // ie requires a double layout on the first pass
41315             if(Roo.isIE && !this.initialized){
41316                 this.initialized = true;
41317                 this.layout.layout();
41318             }
41319         }
41320     },
41321     
41322     // activate all subpanels if not currently active..
41323     
41324     setActiveState : function(active){
41325         this.active = active;
41326         this.setActiveClass(active);
41327         
41328         if(!active){
41329             this.fireEvent("deactivate", this);
41330             return;
41331         }
41332         
41333         this.fireEvent("activate", this);
41334         // not sure if this should happen before or after..
41335         if (!this.layout) {
41336             return; // should not happen..
41337         }
41338         var reg = false;
41339         for (var r in this.layout.regions) {
41340             reg = this.layout.getRegion(r);
41341             if (reg.getActivePanel()) {
41342                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41343                 reg.setActivePanel(reg.getActivePanel());
41344                 continue;
41345             }
41346             if (!reg.panels.length) {
41347                 continue;
41348             }
41349             reg.showPanel(reg.getPanel(0));
41350         }
41351         
41352         
41353         
41354         
41355     },
41356     
41357     /**
41358      * Returns the nested BorderLayout for this panel
41359      * @return {Roo.BorderLayout} 
41360      */
41361     getLayout : function(){
41362         return this.layout;
41363     },
41364     
41365      /**
41366      * Adds a xtype elements to the layout of the nested panel
41367      * <pre><code>
41368
41369 panel.addxtype({
41370        xtype : 'ContentPanel',
41371        region: 'west',
41372        items: [ .... ]
41373    }
41374 );
41375
41376 panel.addxtype({
41377         xtype : 'NestedLayoutPanel',
41378         region: 'west',
41379         layout: {
41380            center: { },
41381            west: { }   
41382         },
41383         items : [ ... list of content panels or nested layout panels.. ]
41384    }
41385 );
41386 </code></pre>
41387      * @param {Object} cfg Xtype definition of item to add.
41388      */
41389     addxtype : function(cfg) {
41390         return this.layout.addxtype(cfg);
41391     
41392     }
41393 });/*
41394  * Based on:
41395  * Ext JS Library 1.1.1
41396  * Copyright(c) 2006-2007, Ext JS, LLC.
41397  *
41398  * Originally Released Under LGPL - original licence link has changed is not relivant.
41399  *
41400  * Fork - LGPL
41401  * <script type="text/javascript">
41402  */
41403 /**
41404  * @class Roo.TabPanel
41405  * @extends Roo.util.Observable
41406  * A lightweight tab container.
41407  * <br><br>
41408  * Usage:
41409  * <pre><code>
41410 // basic tabs 1, built from existing content
41411 var tabs = new Roo.TabPanel("tabs1");
41412 tabs.addTab("script", "View Script");
41413 tabs.addTab("markup", "View Markup");
41414 tabs.activate("script");
41415
41416 // more advanced tabs, built from javascript
41417 var jtabs = new Roo.TabPanel("jtabs");
41418 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41419
41420 // set up the UpdateManager
41421 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41422 var updater = tab2.getUpdateManager();
41423 updater.setDefaultUrl("ajax1.htm");
41424 tab2.on('activate', updater.refresh, updater, true);
41425
41426 // Use setUrl for Ajax loading
41427 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41428 tab3.setUrl("ajax2.htm", null, true);
41429
41430 // Disabled tab
41431 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41432 tab4.disable();
41433
41434 jtabs.activate("jtabs-1");
41435  * </code></pre>
41436  * @constructor
41437  * Create a new TabPanel.
41438  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41439  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41440  */
41441 Roo.bootstrap.panel.Tabs = function(config){
41442     /**
41443     * The container element for this TabPanel.
41444     * @type Roo.Element
41445     */
41446     this.el = Roo.get(config.el);
41447     delete config.el;
41448     if(config){
41449         if(typeof config == "boolean"){
41450             this.tabPosition = config ? "bottom" : "top";
41451         }else{
41452             Roo.apply(this, config);
41453         }
41454     }
41455     
41456     if(this.tabPosition == "bottom"){
41457         // if tabs are at the bottom = create the body first.
41458         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41459         this.el.addClass("roo-tabs-bottom");
41460     }
41461     // next create the tabs holders
41462     
41463     if (this.tabPosition == "west"){
41464         
41465         var reg = this.region; // fake it..
41466         while (reg) {
41467             if (!reg.mgr.parent) {
41468                 break;
41469             }
41470             reg = reg.mgr.parent.region;
41471         }
41472         Roo.log("got nest?");
41473         Roo.log(reg);
41474         if (reg.mgr.getRegion('west')) {
41475             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41476             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41477             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41478             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41479             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41480         
41481             
41482         }
41483         
41484         
41485     } else {
41486      
41487         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41488         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41489         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41490         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41491     }
41492     
41493     
41494     if(Roo.isIE){
41495         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41496     }
41497     
41498     // finally - if tabs are at the top, then create the body last..
41499     if(this.tabPosition != "bottom"){
41500         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41501          * @type Roo.Element
41502          */
41503         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41504         this.el.addClass("roo-tabs-top");
41505     }
41506     this.items = [];
41507
41508     this.bodyEl.setStyle("position", "relative");
41509
41510     this.active = null;
41511     this.activateDelegate = this.activate.createDelegate(this);
41512
41513     this.addEvents({
41514         /**
41515          * @event tabchange
41516          * Fires when the active tab changes
41517          * @param {Roo.TabPanel} this
41518          * @param {Roo.TabPanelItem} activePanel The new active tab
41519          */
41520         "tabchange": true,
41521         /**
41522          * @event beforetabchange
41523          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41524          * @param {Roo.TabPanel} this
41525          * @param {Object} e Set cancel to true on this object to cancel the tab change
41526          * @param {Roo.TabPanelItem} tab The tab being changed to
41527          */
41528         "beforetabchange" : true
41529     });
41530
41531     Roo.EventManager.onWindowResize(this.onResize, this);
41532     this.cpad = this.el.getPadding("lr");
41533     this.hiddenCount = 0;
41534
41535
41536     // toolbar on the tabbar support...
41537     if (this.toolbar) {
41538         alert("no toolbar support yet");
41539         this.toolbar  = false;
41540         /*
41541         var tcfg = this.toolbar;
41542         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41543         this.toolbar = new Roo.Toolbar(tcfg);
41544         if (Roo.isSafari) {
41545             var tbl = tcfg.container.child('table', true);
41546             tbl.setAttribute('width', '100%');
41547         }
41548         */
41549         
41550     }
41551    
41552
41553
41554     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41555 };
41556
41557 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41558     /*
41559      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41560      */
41561     tabPosition : "top",
41562     /*
41563      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41564      */
41565     currentTabWidth : 0,
41566     /*
41567      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41568      */
41569     minTabWidth : 40,
41570     /*
41571      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41572      */
41573     maxTabWidth : 250,
41574     /*
41575      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41576      */
41577     preferredTabWidth : 175,
41578     /*
41579      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41580      */
41581     resizeTabs : false,
41582     /*
41583      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41584      */
41585     monitorResize : true,
41586     /*
41587      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41588      */
41589     toolbar : false,  // set by caller..
41590     
41591     region : false, /// set by caller
41592     
41593     disableTooltips : true, // not used yet...
41594
41595     /**
41596      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41597      * @param {String} id The id of the div to use <b>or create</b>
41598      * @param {String} text The text for the tab
41599      * @param {String} content (optional) Content to put in the TabPanelItem body
41600      * @param {Boolean} closable (optional) True to create a close icon on the tab
41601      * @return {Roo.TabPanelItem} The created TabPanelItem
41602      */
41603     addTab : function(id, text, content, closable, tpl)
41604     {
41605         var item = new Roo.bootstrap.panel.TabItem({
41606             panel: this,
41607             id : id,
41608             text : text,
41609             closable : closable,
41610             tpl : tpl
41611         });
41612         this.addTabItem(item);
41613         if(content){
41614             item.setContent(content);
41615         }
41616         return item;
41617     },
41618
41619     /**
41620      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41621      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41622      * @return {Roo.TabPanelItem}
41623      */
41624     getTab : function(id){
41625         return this.items[id];
41626     },
41627
41628     /**
41629      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41630      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41631      */
41632     hideTab : function(id){
41633         var t = this.items[id];
41634         if(!t.isHidden()){
41635            t.setHidden(true);
41636            this.hiddenCount++;
41637            this.autoSizeTabs();
41638         }
41639     },
41640
41641     /**
41642      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41643      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41644      */
41645     unhideTab : function(id){
41646         var t = this.items[id];
41647         if(t.isHidden()){
41648            t.setHidden(false);
41649            this.hiddenCount--;
41650            this.autoSizeTabs();
41651         }
41652     },
41653
41654     /**
41655      * Adds an existing {@link Roo.TabPanelItem}.
41656      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41657      */
41658     addTabItem : function(item)
41659     {
41660         this.items[item.id] = item;
41661         this.items.push(item);
41662         this.autoSizeTabs();
41663       //  if(this.resizeTabs){
41664     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41665   //         this.autoSizeTabs();
41666 //        }else{
41667 //            item.autoSize();
41668        // }
41669     },
41670
41671     /**
41672      * Removes a {@link Roo.TabPanelItem}.
41673      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41674      */
41675     removeTab : function(id){
41676         var items = this.items;
41677         var tab = items[id];
41678         if(!tab) { return; }
41679         var index = items.indexOf(tab);
41680         if(this.active == tab && items.length > 1){
41681             var newTab = this.getNextAvailable(index);
41682             if(newTab) {
41683                 newTab.activate();
41684             }
41685         }
41686         this.stripEl.dom.removeChild(tab.pnode.dom);
41687         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41688             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41689         }
41690         items.splice(index, 1);
41691         delete this.items[tab.id];
41692         tab.fireEvent("close", tab);
41693         tab.purgeListeners();
41694         this.autoSizeTabs();
41695     },
41696
41697     getNextAvailable : function(start){
41698         var items = this.items;
41699         var index = start;
41700         // look for a next tab that will slide over to
41701         // replace the one being removed
41702         while(index < items.length){
41703             var item = items[++index];
41704             if(item && !item.isHidden()){
41705                 return item;
41706             }
41707         }
41708         // if one isn't found select the previous tab (on the left)
41709         index = start;
41710         while(index >= 0){
41711             var item = items[--index];
41712             if(item && !item.isHidden()){
41713                 return item;
41714             }
41715         }
41716         return null;
41717     },
41718
41719     /**
41720      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41721      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41722      */
41723     disableTab : function(id){
41724         var tab = this.items[id];
41725         if(tab && this.active != tab){
41726             tab.disable();
41727         }
41728     },
41729
41730     /**
41731      * Enables a {@link Roo.TabPanelItem} that is disabled.
41732      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41733      */
41734     enableTab : function(id){
41735         var tab = this.items[id];
41736         tab.enable();
41737     },
41738
41739     /**
41740      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41741      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41742      * @return {Roo.TabPanelItem} The TabPanelItem.
41743      */
41744     activate : function(id)
41745     {
41746         //Roo.log('activite:'  + id);
41747         
41748         var tab = this.items[id];
41749         if(!tab){
41750             return null;
41751         }
41752         if(tab == this.active || tab.disabled){
41753             return tab;
41754         }
41755         var e = {};
41756         this.fireEvent("beforetabchange", this, e, tab);
41757         if(e.cancel !== true && !tab.disabled){
41758             if(this.active){
41759                 this.active.hide();
41760             }
41761             this.active = this.items[id];
41762             this.active.show();
41763             this.fireEvent("tabchange", this, this.active);
41764         }
41765         return tab;
41766     },
41767
41768     /**
41769      * Gets the active {@link Roo.TabPanelItem}.
41770      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41771      */
41772     getActiveTab : function(){
41773         return this.active;
41774     },
41775
41776     /**
41777      * Updates the tab body element to fit the height of the container element
41778      * for overflow scrolling
41779      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41780      */
41781     syncHeight : function(targetHeight){
41782         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41783         var bm = this.bodyEl.getMargins();
41784         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41785         this.bodyEl.setHeight(newHeight);
41786         return newHeight;
41787     },
41788
41789     onResize : function(){
41790         if(this.monitorResize){
41791             this.autoSizeTabs();
41792         }
41793     },
41794
41795     /**
41796      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41797      */
41798     beginUpdate : function(){
41799         this.updating = true;
41800     },
41801
41802     /**
41803      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41804      */
41805     endUpdate : function(){
41806         this.updating = false;
41807         this.autoSizeTabs();
41808     },
41809
41810     /**
41811      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41812      */
41813     autoSizeTabs : function()
41814     {
41815         var count = this.items.length;
41816         var vcount = count - this.hiddenCount;
41817         
41818         if (vcount < 2) {
41819             this.stripEl.hide();
41820         } else {
41821             this.stripEl.show();
41822         }
41823         
41824         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41825             return;
41826         }
41827         
41828         
41829         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41830         var availWidth = Math.floor(w / vcount);
41831         var b = this.stripBody;
41832         if(b.getWidth() > w){
41833             var tabs = this.items;
41834             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41835             if(availWidth < this.minTabWidth){
41836                 /*if(!this.sleft){    // incomplete scrolling code
41837                     this.createScrollButtons();
41838                 }
41839                 this.showScroll();
41840                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41841             }
41842         }else{
41843             if(this.currentTabWidth < this.preferredTabWidth){
41844                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41845             }
41846         }
41847     },
41848
41849     /**
41850      * Returns the number of tabs in this TabPanel.
41851      * @return {Number}
41852      */
41853      getCount : function(){
41854          return this.items.length;
41855      },
41856
41857     /**
41858      * Resizes all the tabs to the passed width
41859      * @param {Number} The new width
41860      */
41861     setTabWidth : function(width){
41862         this.currentTabWidth = width;
41863         for(var i = 0, len = this.items.length; i < len; i++) {
41864                 if(!this.items[i].isHidden()) {
41865                 this.items[i].setWidth(width);
41866             }
41867         }
41868     },
41869
41870     /**
41871      * Destroys this TabPanel
41872      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41873      */
41874     destroy : function(removeEl){
41875         Roo.EventManager.removeResizeListener(this.onResize, this);
41876         for(var i = 0, len = this.items.length; i < len; i++){
41877             this.items[i].purgeListeners();
41878         }
41879         if(removeEl === true){
41880             this.el.update("");
41881             this.el.remove();
41882         }
41883     },
41884     
41885     createStrip : function(container)
41886     {
41887         var strip = document.createElement("nav");
41888         strip.className = Roo.bootstrap.version == 4 ?
41889             "navbar-light bg-light" : 
41890             "navbar navbar-default"; //"x-tabs-wrap";
41891         container.appendChild(strip);
41892         return strip;
41893     },
41894     
41895     createStripList : function(strip)
41896     {
41897         // div wrapper for retard IE
41898         // returns the "tr" element.
41899         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41900         //'<div class="x-tabs-strip-wrap">'+
41901           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41902           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41903         return strip.firstChild; //.firstChild.firstChild.firstChild;
41904     },
41905     createBody : function(container)
41906     {
41907         var body = document.createElement("div");
41908         Roo.id(body, "tab-body");
41909         //Roo.fly(body).addClass("x-tabs-body");
41910         Roo.fly(body).addClass("tab-content");
41911         container.appendChild(body);
41912         return body;
41913     },
41914     createItemBody :function(bodyEl, id){
41915         var body = Roo.getDom(id);
41916         if(!body){
41917             body = document.createElement("div");
41918             body.id = id;
41919         }
41920         //Roo.fly(body).addClass("x-tabs-item-body");
41921         Roo.fly(body).addClass("tab-pane");
41922          bodyEl.insertBefore(body, bodyEl.firstChild);
41923         return body;
41924     },
41925     /** @private */
41926     createStripElements :  function(stripEl, text, closable, tpl)
41927     {
41928         var td = document.createElement("li"); // was td..
41929         td.className = 'nav-item';
41930         
41931         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41932         
41933         
41934         stripEl.appendChild(td);
41935         /*if(closable){
41936             td.className = "x-tabs-closable";
41937             if(!this.closeTpl){
41938                 this.closeTpl = new Roo.Template(
41939                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41940                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41941                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41942                 );
41943             }
41944             var el = this.closeTpl.overwrite(td, {"text": text});
41945             var close = el.getElementsByTagName("div")[0];
41946             var inner = el.getElementsByTagName("em")[0];
41947             return {"el": el, "close": close, "inner": inner};
41948         } else {
41949         */
41950         // not sure what this is..
41951 //            if(!this.tabTpl){
41952                 //this.tabTpl = new Roo.Template(
41953                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41954                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41955                 //);
41956 //                this.tabTpl = new Roo.Template(
41957 //                   '<a href="#">' +
41958 //                   '<span unselectable="on"' +
41959 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41960 //                            ' >{text}</span></a>'
41961 //                );
41962 //                
41963 //            }
41964
41965
41966             var template = tpl || this.tabTpl || false;
41967             
41968             if(!template){
41969                 template =  new Roo.Template(
41970                         Roo.bootstrap.version == 4 ? 
41971                             (
41972                                 '<a class="nav-link" href="#" unselectable="on"' +
41973                                      (this.disableTooltips ? '' : ' title="{text}"') +
41974                                      ' >{text}</a>'
41975                             ) : (
41976                                 '<a class="nav-link" href="#">' +
41977                                 '<span unselectable="on"' +
41978                                          (this.disableTooltips ? '' : ' title="{text}"') +
41979                                     ' >{text}</span></a>'
41980                             )
41981                 );
41982             }
41983             
41984             switch (typeof(template)) {
41985                 case 'object' :
41986                     break;
41987                 case 'string' :
41988                     template = new Roo.Template(template);
41989                     break;
41990                 default :
41991                     break;
41992             }
41993             
41994             var el = template.overwrite(td, {"text": text});
41995             
41996             var inner = el.getElementsByTagName("span")[0];
41997             
41998             return {"el": el, "inner": inner};
41999             
42000     }
42001         
42002     
42003 });
42004
42005 /**
42006  * @class Roo.TabPanelItem
42007  * @extends Roo.util.Observable
42008  * Represents an individual item (tab plus body) in a TabPanel.
42009  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42010  * @param {String} id The id of this TabPanelItem
42011  * @param {String} text The text for the tab of this TabPanelItem
42012  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42013  */
42014 Roo.bootstrap.panel.TabItem = function(config){
42015     /**
42016      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42017      * @type Roo.TabPanel
42018      */
42019     this.tabPanel = config.panel;
42020     /**
42021      * The id for this TabPanelItem
42022      * @type String
42023      */
42024     this.id = config.id;
42025     /** @private */
42026     this.disabled = false;
42027     /** @private */
42028     this.text = config.text;
42029     /** @private */
42030     this.loaded = false;
42031     this.closable = config.closable;
42032
42033     /**
42034      * The body element for this TabPanelItem.
42035      * @type Roo.Element
42036      */
42037     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42038     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42039     this.bodyEl.setStyle("display", "block");
42040     this.bodyEl.setStyle("zoom", "1");
42041     //this.hideAction();
42042
42043     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42044     /** @private */
42045     this.el = Roo.get(els.el);
42046     this.inner = Roo.get(els.inner, true);
42047      this.textEl = Roo.bootstrap.version == 4 ?
42048         this.el : Roo.get(this.el.dom.firstChild, true);
42049
42050     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42051     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42052
42053     
42054 //    this.el.on("mousedown", this.onTabMouseDown, this);
42055     this.el.on("click", this.onTabClick, this);
42056     /** @private */
42057     if(config.closable){
42058         var c = Roo.get(els.close, true);
42059         c.dom.title = this.closeText;
42060         c.addClassOnOver("close-over");
42061         c.on("click", this.closeClick, this);
42062      }
42063
42064     this.addEvents({
42065          /**
42066          * @event activate
42067          * Fires when this tab becomes the active tab.
42068          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42069          * @param {Roo.TabPanelItem} this
42070          */
42071         "activate": true,
42072         /**
42073          * @event beforeclose
42074          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42075          * @param {Roo.TabPanelItem} this
42076          * @param {Object} e Set cancel to true on this object to cancel the close.
42077          */
42078         "beforeclose": true,
42079         /**
42080          * @event close
42081          * Fires when this tab is closed.
42082          * @param {Roo.TabPanelItem} this
42083          */
42084          "close": true,
42085         /**
42086          * @event deactivate
42087          * Fires when this tab is no longer the active tab.
42088          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42089          * @param {Roo.TabPanelItem} this
42090          */
42091          "deactivate" : true
42092     });
42093     this.hidden = false;
42094
42095     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42096 };
42097
42098 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42099            {
42100     purgeListeners : function(){
42101        Roo.util.Observable.prototype.purgeListeners.call(this);
42102        this.el.removeAllListeners();
42103     },
42104     /**
42105      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42106      */
42107     show : function(){
42108         this.status_node.addClass("active");
42109         this.showAction();
42110         if(Roo.isOpera){
42111             this.tabPanel.stripWrap.repaint();
42112         }
42113         this.fireEvent("activate", this.tabPanel, this);
42114     },
42115
42116     /**
42117      * Returns true if this tab is the active tab.
42118      * @return {Boolean}
42119      */
42120     isActive : function(){
42121         return this.tabPanel.getActiveTab() == this;
42122     },
42123
42124     /**
42125      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42126      */
42127     hide : function(){
42128         this.status_node.removeClass("active");
42129         this.hideAction();
42130         this.fireEvent("deactivate", this.tabPanel, this);
42131     },
42132
42133     hideAction : function(){
42134         this.bodyEl.hide();
42135         this.bodyEl.setStyle("position", "absolute");
42136         this.bodyEl.setLeft("-20000px");
42137         this.bodyEl.setTop("-20000px");
42138     },
42139
42140     showAction : function(){
42141         this.bodyEl.setStyle("position", "relative");
42142         this.bodyEl.setTop("");
42143         this.bodyEl.setLeft("");
42144         this.bodyEl.show();
42145     },
42146
42147     /**
42148      * Set the tooltip for the tab.
42149      * @param {String} tooltip The tab's tooltip
42150      */
42151     setTooltip : function(text){
42152         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42153             this.textEl.dom.qtip = text;
42154             this.textEl.dom.removeAttribute('title');
42155         }else{
42156             this.textEl.dom.title = text;
42157         }
42158     },
42159
42160     onTabClick : function(e){
42161         e.preventDefault();
42162         this.tabPanel.activate(this.id);
42163     },
42164
42165     onTabMouseDown : function(e){
42166         e.preventDefault();
42167         this.tabPanel.activate(this.id);
42168     },
42169 /*
42170     getWidth : function(){
42171         return this.inner.getWidth();
42172     },
42173
42174     setWidth : function(width){
42175         var iwidth = width - this.linode.getPadding("lr");
42176         this.inner.setWidth(iwidth);
42177         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42178         this.linode.setWidth(width);
42179     },
42180 */
42181     /**
42182      * Show or hide the tab
42183      * @param {Boolean} hidden True to hide or false to show.
42184      */
42185     setHidden : function(hidden){
42186         this.hidden = hidden;
42187         this.linode.setStyle("display", hidden ? "none" : "");
42188     },
42189
42190     /**
42191      * Returns true if this tab is "hidden"
42192      * @return {Boolean}
42193      */
42194     isHidden : function(){
42195         return this.hidden;
42196     },
42197
42198     /**
42199      * Returns the text for this tab
42200      * @return {String}
42201      */
42202     getText : function(){
42203         return this.text;
42204     },
42205     /*
42206     autoSize : function(){
42207         //this.el.beginMeasure();
42208         this.textEl.setWidth(1);
42209         /*
42210          *  #2804 [new] Tabs in Roojs
42211          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42212          */
42213         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42214         //this.el.endMeasure();
42215     //},
42216
42217     /**
42218      * Sets the text for the tab (Note: this also sets the tooltip text)
42219      * @param {String} text The tab's text and tooltip
42220      */
42221     setText : function(text){
42222         this.text = text;
42223         this.textEl.update(text);
42224         this.setTooltip(text);
42225         //if(!this.tabPanel.resizeTabs){
42226         //    this.autoSize();
42227         //}
42228     },
42229     /**
42230      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42231      */
42232     activate : function(){
42233         this.tabPanel.activate(this.id);
42234     },
42235
42236     /**
42237      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42238      */
42239     disable : function(){
42240         if(this.tabPanel.active != this){
42241             this.disabled = true;
42242             this.status_node.addClass("disabled");
42243         }
42244     },
42245
42246     /**
42247      * Enables this TabPanelItem if it was previously disabled.
42248      */
42249     enable : function(){
42250         this.disabled = false;
42251         this.status_node.removeClass("disabled");
42252     },
42253
42254     /**
42255      * Sets the content for this TabPanelItem.
42256      * @param {String} content The content
42257      * @param {Boolean} loadScripts true to look for and load scripts
42258      */
42259     setContent : function(content, loadScripts){
42260         this.bodyEl.update(content, loadScripts);
42261     },
42262
42263     /**
42264      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42265      * @return {Roo.UpdateManager} The UpdateManager
42266      */
42267     getUpdateManager : function(){
42268         return this.bodyEl.getUpdateManager();
42269     },
42270
42271     /**
42272      * Set a URL to be used to load the content for this TabPanelItem.
42273      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42274      * @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)
42275      * @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)
42276      * @return {Roo.UpdateManager} The UpdateManager
42277      */
42278     setUrl : function(url, params, loadOnce){
42279         if(this.refreshDelegate){
42280             this.un('activate', this.refreshDelegate);
42281         }
42282         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42283         this.on("activate", this.refreshDelegate);
42284         return this.bodyEl.getUpdateManager();
42285     },
42286
42287     /** @private */
42288     _handleRefresh : function(url, params, loadOnce){
42289         if(!loadOnce || !this.loaded){
42290             var updater = this.bodyEl.getUpdateManager();
42291             updater.update(url, params, this._setLoaded.createDelegate(this));
42292         }
42293     },
42294
42295     /**
42296      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42297      *   Will fail silently if the setUrl method has not been called.
42298      *   This does not activate the panel, just updates its content.
42299      */
42300     refresh : function(){
42301         if(this.refreshDelegate){
42302            this.loaded = false;
42303            this.refreshDelegate();
42304         }
42305     },
42306
42307     /** @private */
42308     _setLoaded : function(){
42309         this.loaded = true;
42310     },
42311
42312     /** @private */
42313     closeClick : function(e){
42314         var o = {};
42315         e.stopEvent();
42316         this.fireEvent("beforeclose", this, o);
42317         if(o.cancel !== true){
42318             this.tabPanel.removeTab(this.id);
42319         }
42320     },
42321     /**
42322      * The text displayed in the tooltip for the close icon.
42323      * @type String
42324      */
42325     closeText : "Close this tab"
42326 });
42327 /**
42328 *    This script refer to:
42329 *    Title: International Telephone Input
42330 *    Author: Jack O'Connor
42331 *    Code version:  v12.1.12
42332 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42333 **/
42334
42335 Roo.bootstrap.PhoneInputData = function() {
42336     var d = [
42337       [
42338         "Afghanistan (‫افغانستان‬‎)",
42339         "af",
42340         "93"
42341       ],
42342       [
42343         "Albania (Shqipëri)",
42344         "al",
42345         "355"
42346       ],
42347       [
42348         "Algeria (‫الجزائر‬‎)",
42349         "dz",
42350         "213"
42351       ],
42352       [
42353         "American Samoa",
42354         "as",
42355         "1684"
42356       ],
42357       [
42358         "Andorra",
42359         "ad",
42360         "376"
42361       ],
42362       [
42363         "Angola",
42364         "ao",
42365         "244"
42366       ],
42367       [
42368         "Anguilla",
42369         "ai",
42370         "1264"
42371       ],
42372       [
42373         "Antigua and Barbuda",
42374         "ag",
42375         "1268"
42376       ],
42377       [
42378         "Argentina",
42379         "ar",
42380         "54"
42381       ],
42382       [
42383         "Armenia (Հայաստան)",
42384         "am",
42385         "374"
42386       ],
42387       [
42388         "Aruba",
42389         "aw",
42390         "297"
42391       ],
42392       [
42393         "Australia",
42394         "au",
42395         "61",
42396         0
42397       ],
42398       [
42399         "Austria (Österreich)",
42400         "at",
42401         "43"
42402       ],
42403       [
42404         "Azerbaijan (Azərbaycan)",
42405         "az",
42406         "994"
42407       ],
42408       [
42409         "Bahamas",
42410         "bs",
42411         "1242"
42412       ],
42413       [
42414         "Bahrain (‫البحرين‬‎)",
42415         "bh",
42416         "973"
42417       ],
42418       [
42419         "Bangladesh (বাংলাদেশ)",
42420         "bd",
42421         "880"
42422       ],
42423       [
42424         "Barbados",
42425         "bb",
42426         "1246"
42427       ],
42428       [
42429         "Belarus (Беларусь)",
42430         "by",
42431         "375"
42432       ],
42433       [
42434         "Belgium (België)",
42435         "be",
42436         "32"
42437       ],
42438       [
42439         "Belize",
42440         "bz",
42441         "501"
42442       ],
42443       [
42444         "Benin (Bénin)",
42445         "bj",
42446         "229"
42447       ],
42448       [
42449         "Bermuda",
42450         "bm",
42451         "1441"
42452       ],
42453       [
42454         "Bhutan (འབྲུག)",
42455         "bt",
42456         "975"
42457       ],
42458       [
42459         "Bolivia",
42460         "bo",
42461         "591"
42462       ],
42463       [
42464         "Bosnia and Herzegovina (Босна и Херцеговина)",
42465         "ba",
42466         "387"
42467       ],
42468       [
42469         "Botswana",
42470         "bw",
42471         "267"
42472       ],
42473       [
42474         "Brazil (Brasil)",
42475         "br",
42476         "55"
42477       ],
42478       [
42479         "British Indian Ocean Territory",
42480         "io",
42481         "246"
42482       ],
42483       [
42484         "British Virgin Islands",
42485         "vg",
42486         "1284"
42487       ],
42488       [
42489         "Brunei",
42490         "bn",
42491         "673"
42492       ],
42493       [
42494         "Bulgaria (България)",
42495         "bg",
42496         "359"
42497       ],
42498       [
42499         "Burkina Faso",
42500         "bf",
42501         "226"
42502       ],
42503       [
42504         "Burundi (Uburundi)",
42505         "bi",
42506         "257"
42507       ],
42508       [
42509         "Cambodia (កម្ពុជា)",
42510         "kh",
42511         "855"
42512       ],
42513       [
42514         "Cameroon (Cameroun)",
42515         "cm",
42516         "237"
42517       ],
42518       [
42519         "Canada",
42520         "ca",
42521         "1",
42522         1,
42523         ["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"]
42524       ],
42525       [
42526         "Cape Verde (Kabu Verdi)",
42527         "cv",
42528         "238"
42529       ],
42530       [
42531         "Caribbean Netherlands",
42532         "bq",
42533         "599",
42534         1
42535       ],
42536       [
42537         "Cayman Islands",
42538         "ky",
42539         "1345"
42540       ],
42541       [
42542         "Central African Republic (République centrafricaine)",
42543         "cf",
42544         "236"
42545       ],
42546       [
42547         "Chad (Tchad)",
42548         "td",
42549         "235"
42550       ],
42551       [
42552         "Chile",
42553         "cl",
42554         "56"
42555       ],
42556       [
42557         "China (中国)",
42558         "cn",
42559         "86"
42560       ],
42561       [
42562         "Christmas Island",
42563         "cx",
42564         "61",
42565         2
42566       ],
42567       [
42568         "Cocos (Keeling) Islands",
42569         "cc",
42570         "61",
42571         1
42572       ],
42573       [
42574         "Colombia",
42575         "co",
42576         "57"
42577       ],
42578       [
42579         "Comoros (‫جزر القمر‬‎)",
42580         "km",
42581         "269"
42582       ],
42583       [
42584         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42585         "cd",
42586         "243"
42587       ],
42588       [
42589         "Congo (Republic) (Congo-Brazzaville)",
42590         "cg",
42591         "242"
42592       ],
42593       [
42594         "Cook Islands",
42595         "ck",
42596         "682"
42597       ],
42598       [
42599         "Costa Rica",
42600         "cr",
42601         "506"
42602       ],
42603       [
42604         "Côte d’Ivoire",
42605         "ci",
42606         "225"
42607       ],
42608       [
42609         "Croatia (Hrvatska)",
42610         "hr",
42611         "385"
42612       ],
42613       [
42614         "Cuba",
42615         "cu",
42616         "53"
42617       ],
42618       [
42619         "Curaçao",
42620         "cw",
42621         "599",
42622         0
42623       ],
42624       [
42625         "Cyprus (Κύπρος)",
42626         "cy",
42627         "357"
42628       ],
42629       [
42630         "Czech Republic (Česká republika)",
42631         "cz",
42632         "420"
42633       ],
42634       [
42635         "Denmark (Danmark)",
42636         "dk",
42637         "45"
42638       ],
42639       [
42640         "Djibouti",
42641         "dj",
42642         "253"
42643       ],
42644       [
42645         "Dominica",
42646         "dm",
42647         "1767"
42648       ],
42649       [
42650         "Dominican Republic (República Dominicana)",
42651         "do",
42652         "1",
42653         2,
42654         ["809", "829", "849"]
42655       ],
42656       [
42657         "Ecuador",
42658         "ec",
42659         "593"
42660       ],
42661       [
42662         "Egypt (‫مصر‬‎)",
42663         "eg",
42664         "20"
42665       ],
42666       [
42667         "El Salvador",
42668         "sv",
42669         "503"
42670       ],
42671       [
42672         "Equatorial Guinea (Guinea Ecuatorial)",
42673         "gq",
42674         "240"
42675       ],
42676       [
42677         "Eritrea",
42678         "er",
42679         "291"
42680       ],
42681       [
42682         "Estonia (Eesti)",
42683         "ee",
42684         "372"
42685       ],
42686       [
42687         "Ethiopia",
42688         "et",
42689         "251"
42690       ],
42691       [
42692         "Falkland Islands (Islas Malvinas)",
42693         "fk",
42694         "500"
42695       ],
42696       [
42697         "Faroe Islands (Føroyar)",
42698         "fo",
42699         "298"
42700       ],
42701       [
42702         "Fiji",
42703         "fj",
42704         "679"
42705       ],
42706       [
42707         "Finland (Suomi)",
42708         "fi",
42709         "358",
42710         0
42711       ],
42712       [
42713         "France",
42714         "fr",
42715         "33"
42716       ],
42717       [
42718         "French Guiana (Guyane française)",
42719         "gf",
42720         "594"
42721       ],
42722       [
42723         "French Polynesia (Polynésie française)",
42724         "pf",
42725         "689"
42726       ],
42727       [
42728         "Gabon",
42729         "ga",
42730         "241"
42731       ],
42732       [
42733         "Gambia",
42734         "gm",
42735         "220"
42736       ],
42737       [
42738         "Georgia (საქართველო)",
42739         "ge",
42740         "995"
42741       ],
42742       [
42743         "Germany (Deutschland)",
42744         "de",
42745         "49"
42746       ],
42747       [
42748         "Ghana (Gaana)",
42749         "gh",
42750         "233"
42751       ],
42752       [
42753         "Gibraltar",
42754         "gi",
42755         "350"
42756       ],
42757       [
42758         "Greece (Ελλάδα)",
42759         "gr",
42760         "30"
42761       ],
42762       [
42763         "Greenland (Kalaallit Nunaat)",
42764         "gl",
42765         "299"
42766       ],
42767       [
42768         "Grenada",
42769         "gd",
42770         "1473"
42771       ],
42772       [
42773         "Guadeloupe",
42774         "gp",
42775         "590",
42776         0
42777       ],
42778       [
42779         "Guam",
42780         "gu",
42781         "1671"
42782       ],
42783       [
42784         "Guatemala",
42785         "gt",
42786         "502"
42787       ],
42788       [
42789         "Guernsey",
42790         "gg",
42791         "44",
42792         1
42793       ],
42794       [
42795         "Guinea (Guinée)",
42796         "gn",
42797         "224"
42798       ],
42799       [
42800         "Guinea-Bissau (Guiné Bissau)",
42801         "gw",
42802         "245"
42803       ],
42804       [
42805         "Guyana",
42806         "gy",
42807         "592"
42808       ],
42809       [
42810         "Haiti",
42811         "ht",
42812         "509"
42813       ],
42814       [
42815         "Honduras",
42816         "hn",
42817         "504"
42818       ],
42819       [
42820         "Hong Kong (香港)",
42821         "hk",
42822         "852"
42823       ],
42824       [
42825         "Hungary (Magyarország)",
42826         "hu",
42827         "36"
42828       ],
42829       [
42830         "Iceland (Ísland)",
42831         "is",
42832         "354"
42833       ],
42834       [
42835         "India (भारत)",
42836         "in",
42837         "91"
42838       ],
42839       [
42840         "Indonesia",
42841         "id",
42842         "62"
42843       ],
42844       [
42845         "Iran (‫ایران‬‎)",
42846         "ir",
42847         "98"
42848       ],
42849       [
42850         "Iraq (‫العراق‬‎)",
42851         "iq",
42852         "964"
42853       ],
42854       [
42855         "Ireland",
42856         "ie",
42857         "353"
42858       ],
42859       [
42860         "Isle of Man",
42861         "im",
42862         "44",
42863         2
42864       ],
42865       [
42866         "Israel (‫ישראל‬‎)",
42867         "il",
42868         "972"
42869       ],
42870       [
42871         "Italy (Italia)",
42872         "it",
42873         "39",
42874         0
42875       ],
42876       [
42877         "Jamaica",
42878         "jm",
42879         "1876"
42880       ],
42881       [
42882         "Japan (日本)",
42883         "jp",
42884         "81"
42885       ],
42886       [
42887         "Jersey",
42888         "je",
42889         "44",
42890         3
42891       ],
42892       [
42893         "Jordan (‫الأردن‬‎)",
42894         "jo",
42895         "962"
42896       ],
42897       [
42898         "Kazakhstan (Казахстан)",
42899         "kz",
42900         "7",
42901         1
42902       ],
42903       [
42904         "Kenya",
42905         "ke",
42906         "254"
42907       ],
42908       [
42909         "Kiribati",
42910         "ki",
42911         "686"
42912       ],
42913       [
42914         "Kosovo",
42915         "xk",
42916         "383"
42917       ],
42918       [
42919         "Kuwait (‫الكويت‬‎)",
42920         "kw",
42921         "965"
42922       ],
42923       [
42924         "Kyrgyzstan (Кыргызстан)",
42925         "kg",
42926         "996"
42927       ],
42928       [
42929         "Laos (ລາວ)",
42930         "la",
42931         "856"
42932       ],
42933       [
42934         "Latvia (Latvija)",
42935         "lv",
42936         "371"
42937       ],
42938       [
42939         "Lebanon (‫لبنان‬‎)",
42940         "lb",
42941         "961"
42942       ],
42943       [
42944         "Lesotho",
42945         "ls",
42946         "266"
42947       ],
42948       [
42949         "Liberia",
42950         "lr",
42951         "231"
42952       ],
42953       [
42954         "Libya (‫ليبيا‬‎)",
42955         "ly",
42956         "218"
42957       ],
42958       [
42959         "Liechtenstein",
42960         "li",
42961         "423"
42962       ],
42963       [
42964         "Lithuania (Lietuva)",
42965         "lt",
42966         "370"
42967       ],
42968       [
42969         "Luxembourg",
42970         "lu",
42971         "352"
42972       ],
42973       [
42974         "Macau (澳門)",
42975         "mo",
42976         "853"
42977       ],
42978       [
42979         "Macedonia (FYROM) (Македонија)",
42980         "mk",
42981         "389"
42982       ],
42983       [
42984         "Madagascar (Madagasikara)",
42985         "mg",
42986         "261"
42987       ],
42988       [
42989         "Malawi",
42990         "mw",
42991         "265"
42992       ],
42993       [
42994         "Malaysia",
42995         "my",
42996         "60"
42997       ],
42998       [
42999         "Maldives",
43000         "mv",
43001         "960"
43002       ],
43003       [
43004         "Mali",
43005         "ml",
43006         "223"
43007       ],
43008       [
43009         "Malta",
43010         "mt",
43011         "356"
43012       ],
43013       [
43014         "Marshall Islands",
43015         "mh",
43016         "692"
43017       ],
43018       [
43019         "Martinique",
43020         "mq",
43021         "596"
43022       ],
43023       [
43024         "Mauritania (‫موريتانيا‬‎)",
43025         "mr",
43026         "222"
43027       ],
43028       [
43029         "Mauritius (Moris)",
43030         "mu",
43031         "230"
43032       ],
43033       [
43034         "Mayotte",
43035         "yt",
43036         "262",
43037         1
43038       ],
43039       [
43040         "Mexico (México)",
43041         "mx",
43042         "52"
43043       ],
43044       [
43045         "Micronesia",
43046         "fm",
43047         "691"
43048       ],
43049       [
43050         "Moldova (Republica Moldova)",
43051         "md",
43052         "373"
43053       ],
43054       [
43055         "Monaco",
43056         "mc",
43057         "377"
43058       ],
43059       [
43060         "Mongolia (Монгол)",
43061         "mn",
43062         "976"
43063       ],
43064       [
43065         "Montenegro (Crna Gora)",
43066         "me",
43067         "382"
43068       ],
43069       [
43070         "Montserrat",
43071         "ms",
43072         "1664"
43073       ],
43074       [
43075         "Morocco (‫المغرب‬‎)",
43076         "ma",
43077         "212",
43078         0
43079       ],
43080       [
43081         "Mozambique (Moçambique)",
43082         "mz",
43083         "258"
43084       ],
43085       [
43086         "Myanmar (Burma) (မြန်မာ)",
43087         "mm",
43088         "95"
43089       ],
43090       [
43091         "Namibia (Namibië)",
43092         "na",
43093         "264"
43094       ],
43095       [
43096         "Nauru",
43097         "nr",
43098         "674"
43099       ],
43100       [
43101         "Nepal (नेपाल)",
43102         "np",
43103         "977"
43104       ],
43105       [
43106         "Netherlands (Nederland)",
43107         "nl",
43108         "31"
43109       ],
43110       [
43111         "New Caledonia (Nouvelle-Calédonie)",
43112         "nc",
43113         "687"
43114       ],
43115       [
43116         "New Zealand",
43117         "nz",
43118         "64"
43119       ],
43120       [
43121         "Nicaragua",
43122         "ni",
43123         "505"
43124       ],
43125       [
43126         "Niger (Nijar)",
43127         "ne",
43128         "227"
43129       ],
43130       [
43131         "Nigeria",
43132         "ng",
43133         "234"
43134       ],
43135       [
43136         "Niue",
43137         "nu",
43138         "683"
43139       ],
43140       [
43141         "Norfolk Island",
43142         "nf",
43143         "672"
43144       ],
43145       [
43146         "North Korea (조선 민주주의 인민 공화국)",
43147         "kp",
43148         "850"
43149       ],
43150       [
43151         "Northern Mariana Islands",
43152         "mp",
43153         "1670"
43154       ],
43155       [
43156         "Norway (Norge)",
43157         "no",
43158         "47",
43159         0
43160       ],
43161       [
43162         "Oman (‫عُمان‬‎)",
43163         "om",
43164         "968"
43165       ],
43166       [
43167         "Pakistan (‫پاکستان‬‎)",
43168         "pk",
43169         "92"
43170       ],
43171       [
43172         "Palau",
43173         "pw",
43174         "680"
43175       ],
43176       [
43177         "Palestine (‫فلسطين‬‎)",
43178         "ps",
43179         "970"
43180       ],
43181       [
43182         "Panama (Panamá)",
43183         "pa",
43184         "507"
43185       ],
43186       [
43187         "Papua New Guinea",
43188         "pg",
43189         "675"
43190       ],
43191       [
43192         "Paraguay",
43193         "py",
43194         "595"
43195       ],
43196       [
43197         "Peru (Perú)",
43198         "pe",
43199         "51"
43200       ],
43201       [
43202         "Philippines",
43203         "ph",
43204         "63"
43205       ],
43206       [
43207         "Poland (Polska)",
43208         "pl",
43209         "48"
43210       ],
43211       [
43212         "Portugal",
43213         "pt",
43214         "351"
43215       ],
43216       [
43217         "Puerto Rico",
43218         "pr",
43219         "1",
43220         3,
43221         ["787", "939"]
43222       ],
43223       [
43224         "Qatar (‫قطر‬‎)",
43225         "qa",
43226         "974"
43227       ],
43228       [
43229         "Réunion (La Réunion)",
43230         "re",
43231         "262",
43232         0
43233       ],
43234       [
43235         "Romania (România)",
43236         "ro",
43237         "40"
43238       ],
43239       [
43240         "Russia (Россия)",
43241         "ru",
43242         "7",
43243         0
43244       ],
43245       [
43246         "Rwanda",
43247         "rw",
43248         "250"
43249       ],
43250       [
43251         "Saint Barthélemy",
43252         "bl",
43253         "590",
43254         1
43255       ],
43256       [
43257         "Saint Helena",
43258         "sh",
43259         "290"
43260       ],
43261       [
43262         "Saint Kitts and Nevis",
43263         "kn",
43264         "1869"
43265       ],
43266       [
43267         "Saint Lucia",
43268         "lc",
43269         "1758"
43270       ],
43271       [
43272         "Saint Martin (Saint-Martin (partie française))",
43273         "mf",
43274         "590",
43275         2
43276       ],
43277       [
43278         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43279         "pm",
43280         "508"
43281       ],
43282       [
43283         "Saint Vincent and the Grenadines",
43284         "vc",
43285         "1784"
43286       ],
43287       [
43288         "Samoa",
43289         "ws",
43290         "685"
43291       ],
43292       [
43293         "San Marino",
43294         "sm",
43295         "378"
43296       ],
43297       [
43298         "São Tomé and Príncipe (São Tomé e Príncipe)",
43299         "st",
43300         "239"
43301       ],
43302       [
43303         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43304         "sa",
43305         "966"
43306       ],
43307       [
43308         "Senegal (Sénégal)",
43309         "sn",
43310         "221"
43311       ],
43312       [
43313         "Serbia (Србија)",
43314         "rs",
43315         "381"
43316       ],
43317       [
43318         "Seychelles",
43319         "sc",
43320         "248"
43321       ],
43322       [
43323         "Sierra Leone",
43324         "sl",
43325         "232"
43326       ],
43327       [
43328         "Singapore",
43329         "sg",
43330         "65"
43331       ],
43332       [
43333         "Sint Maarten",
43334         "sx",
43335         "1721"
43336       ],
43337       [
43338         "Slovakia (Slovensko)",
43339         "sk",
43340         "421"
43341       ],
43342       [
43343         "Slovenia (Slovenija)",
43344         "si",
43345         "386"
43346       ],
43347       [
43348         "Solomon Islands",
43349         "sb",
43350         "677"
43351       ],
43352       [
43353         "Somalia (Soomaaliya)",
43354         "so",
43355         "252"
43356       ],
43357       [
43358         "South Africa",
43359         "za",
43360         "27"
43361       ],
43362       [
43363         "South Korea (대한민국)",
43364         "kr",
43365         "82"
43366       ],
43367       [
43368         "South Sudan (‫جنوب السودان‬‎)",
43369         "ss",
43370         "211"
43371       ],
43372       [
43373         "Spain (España)",
43374         "es",
43375         "34"
43376       ],
43377       [
43378         "Sri Lanka (ශ්‍රී ලංකාව)",
43379         "lk",
43380         "94"
43381       ],
43382       [
43383         "Sudan (‫السودان‬‎)",
43384         "sd",
43385         "249"
43386       ],
43387       [
43388         "Suriname",
43389         "sr",
43390         "597"
43391       ],
43392       [
43393         "Svalbard and Jan Mayen",
43394         "sj",
43395         "47",
43396         1
43397       ],
43398       [
43399         "Swaziland",
43400         "sz",
43401         "268"
43402       ],
43403       [
43404         "Sweden (Sverige)",
43405         "se",
43406         "46"
43407       ],
43408       [
43409         "Switzerland (Schweiz)",
43410         "ch",
43411         "41"
43412       ],
43413       [
43414         "Syria (‫سوريا‬‎)",
43415         "sy",
43416         "963"
43417       ],
43418       [
43419         "Taiwan (台灣)",
43420         "tw",
43421         "886"
43422       ],
43423       [
43424         "Tajikistan",
43425         "tj",
43426         "992"
43427       ],
43428       [
43429         "Tanzania",
43430         "tz",
43431         "255"
43432       ],
43433       [
43434         "Thailand (ไทย)",
43435         "th",
43436         "66"
43437       ],
43438       [
43439         "Timor-Leste",
43440         "tl",
43441         "670"
43442       ],
43443       [
43444         "Togo",
43445         "tg",
43446         "228"
43447       ],
43448       [
43449         "Tokelau",
43450         "tk",
43451         "690"
43452       ],
43453       [
43454         "Tonga",
43455         "to",
43456         "676"
43457       ],
43458       [
43459         "Trinidad and Tobago",
43460         "tt",
43461         "1868"
43462       ],
43463       [
43464         "Tunisia (‫تونس‬‎)",
43465         "tn",
43466         "216"
43467       ],
43468       [
43469         "Turkey (Türkiye)",
43470         "tr",
43471         "90"
43472       ],
43473       [
43474         "Turkmenistan",
43475         "tm",
43476         "993"
43477       ],
43478       [
43479         "Turks and Caicos Islands",
43480         "tc",
43481         "1649"
43482       ],
43483       [
43484         "Tuvalu",
43485         "tv",
43486         "688"
43487       ],
43488       [
43489         "U.S. Virgin Islands",
43490         "vi",
43491         "1340"
43492       ],
43493       [
43494         "Uganda",
43495         "ug",
43496         "256"
43497       ],
43498       [
43499         "Ukraine (Україна)",
43500         "ua",
43501         "380"
43502       ],
43503       [
43504         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43505         "ae",
43506         "971"
43507       ],
43508       [
43509         "United Kingdom",
43510         "gb",
43511         "44",
43512         0
43513       ],
43514       [
43515         "United States",
43516         "us",
43517         "1",
43518         0
43519       ],
43520       [
43521         "Uruguay",
43522         "uy",
43523         "598"
43524       ],
43525       [
43526         "Uzbekistan (Oʻzbekiston)",
43527         "uz",
43528         "998"
43529       ],
43530       [
43531         "Vanuatu",
43532         "vu",
43533         "678"
43534       ],
43535       [
43536         "Vatican City (Città del Vaticano)",
43537         "va",
43538         "39",
43539         1
43540       ],
43541       [
43542         "Venezuela",
43543         "ve",
43544         "58"
43545       ],
43546       [
43547         "Vietnam (Việt Nam)",
43548         "vn",
43549         "84"
43550       ],
43551       [
43552         "Wallis and Futuna (Wallis-et-Futuna)",
43553         "wf",
43554         "681"
43555       ],
43556       [
43557         "Western Sahara (‫الصحراء الغربية‬‎)",
43558         "eh",
43559         "212",
43560         1
43561       ],
43562       [
43563         "Yemen (‫اليمن‬‎)",
43564         "ye",
43565         "967"
43566       ],
43567       [
43568         "Zambia",
43569         "zm",
43570         "260"
43571       ],
43572       [
43573         "Zimbabwe",
43574         "zw",
43575         "263"
43576       ],
43577       [
43578         "Åland Islands",
43579         "ax",
43580         "358",
43581         1
43582       ]
43583   ];
43584   
43585   return d;
43586 }/**
43587 *    This script refer to:
43588 *    Title: International Telephone Input
43589 *    Author: Jack O'Connor
43590 *    Code version:  v12.1.12
43591 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43592 **/
43593
43594 /**
43595  * @class Roo.bootstrap.PhoneInput
43596  * @extends Roo.bootstrap.TriggerField
43597  * An input with International dial-code selection
43598  
43599  * @cfg {String} defaultDialCode default '+852'
43600  * @cfg {Array} preferedCountries default []
43601   
43602  * @constructor
43603  * Create a new PhoneInput.
43604  * @param {Object} config Configuration options
43605  */
43606
43607 Roo.bootstrap.PhoneInput = function(config) {
43608     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43609 };
43610
43611 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43612         
43613         listWidth: undefined,
43614         
43615         selectedClass: 'active',
43616         
43617         invalidClass : "has-warning",
43618         
43619         validClass: 'has-success',
43620         
43621         allowed: '0123456789',
43622         
43623         max_length: 15,
43624         
43625         /**
43626          * @cfg {String} defaultDialCode The default dial code when initializing the input
43627          */
43628         defaultDialCode: '+852',
43629         
43630         /**
43631          * @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
43632          */
43633         preferedCountries: false,
43634         
43635         getAutoCreate : function()
43636         {
43637             var data = Roo.bootstrap.PhoneInputData();
43638             var align = this.labelAlign || this.parentLabelAlign();
43639             var id = Roo.id();
43640             
43641             this.allCountries = [];
43642             this.dialCodeMapping = [];
43643             
43644             for (var i = 0; i < data.length; i++) {
43645               var c = data[i];
43646               this.allCountries[i] = {
43647                 name: c[0],
43648                 iso2: c[1],
43649                 dialCode: c[2],
43650                 priority: c[3] || 0,
43651                 areaCodes: c[4] || null
43652               };
43653               this.dialCodeMapping[c[2]] = {
43654                   name: c[0],
43655                   iso2: c[1],
43656                   priority: c[3] || 0,
43657                   areaCodes: c[4] || null
43658               };
43659             }
43660             
43661             var cfg = {
43662                 cls: 'form-group',
43663                 cn: []
43664             };
43665             
43666             var input =  {
43667                 tag: 'input',
43668                 id : id,
43669                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43670                 maxlength: this.max_length,
43671                 cls : 'form-control tel-input',
43672                 autocomplete: 'new-password'
43673             };
43674             
43675             var hiddenInput = {
43676                 tag: 'input',
43677                 type: 'hidden',
43678                 cls: 'hidden-tel-input'
43679             };
43680             
43681             if (this.name) {
43682                 hiddenInput.name = this.name;
43683             }
43684             
43685             if (this.disabled) {
43686                 input.disabled = true;
43687             }
43688             
43689             var flag_container = {
43690                 tag: 'div',
43691                 cls: 'flag-box',
43692                 cn: [
43693                     {
43694                         tag: 'div',
43695                         cls: 'flag'
43696                     },
43697                     {
43698                         tag: 'div',
43699                         cls: 'caret'
43700                     }
43701                 ]
43702             };
43703             
43704             var box = {
43705                 tag: 'div',
43706                 cls: this.hasFeedback ? 'has-feedback' : '',
43707                 cn: [
43708                     hiddenInput,
43709                     input,
43710                     {
43711                         tag: 'input',
43712                         cls: 'dial-code-holder',
43713                         disabled: true
43714                     }
43715                 ]
43716             };
43717             
43718             var container = {
43719                 cls: 'roo-select2-container input-group',
43720                 cn: [
43721                     flag_container,
43722                     box
43723                 ]
43724             };
43725             
43726             if (this.fieldLabel.length) {
43727                 var indicator = {
43728                     tag: 'i',
43729                     tooltip: 'This field is required'
43730                 };
43731                 
43732                 var label = {
43733                     tag: 'label',
43734                     'for':  id,
43735                     cls: 'control-label',
43736                     cn: []
43737                 };
43738                 
43739                 var label_text = {
43740                     tag: 'span',
43741                     html: this.fieldLabel
43742                 };
43743                 
43744                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43745                 label.cn = [
43746                     indicator,
43747                     label_text
43748                 ];
43749                 
43750                 if(this.indicatorpos == 'right') {
43751                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43752                     label.cn = [
43753                         label_text,
43754                         indicator
43755                     ];
43756                 }
43757                 
43758                 if(align == 'left') {
43759                     container = {
43760                         tag: 'div',
43761                         cn: [
43762                             container
43763                         ]
43764                     };
43765                     
43766                     if(this.labelWidth > 12){
43767                         label.style = "width: " + this.labelWidth + 'px';
43768                     }
43769                     if(this.labelWidth < 13 && this.labelmd == 0){
43770                         this.labelmd = this.labelWidth;
43771                     }
43772                     if(this.labellg > 0){
43773                         label.cls += ' col-lg-' + this.labellg;
43774                         input.cls += ' col-lg-' + (12 - this.labellg);
43775                     }
43776                     if(this.labelmd > 0){
43777                         label.cls += ' col-md-' + this.labelmd;
43778                         container.cls += ' col-md-' + (12 - this.labelmd);
43779                     }
43780                     if(this.labelsm > 0){
43781                         label.cls += ' col-sm-' + this.labelsm;
43782                         container.cls += ' col-sm-' + (12 - this.labelsm);
43783                     }
43784                     if(this.labelxs > 0){
43785                         label.cls += ' col-xs-' + this.labelxs;
43786                         container.cls += ' col-xs-' + (12 - this.labelxs);
43787                     }
43788                 }
43789             }
43790             
43791             cfg.cn = [
43792                 label,
43793                 container
43794             ];
43795             
43796             var settings = this;
43797             
43798             ['xs','sm','md','lg'].map(function(size){
43799                 if (settings[size]) {
43800                     cfg.cls += ' col-' + size + '-' + settings[size];
43801                 }
43802             });
43803             
43804             this.store = new Roo.data.Store({
43805                 proxy : new Roo.data.MemoryProxy({}),
43806                 reader : new Roo.data.JsonReader({
43807                     fields : [
43808                         {
43809                             'name' : 'name',
43810                             'type' : 'string'
43811                         },
43812                         {
43813                             'name' : 'iso2',
43814                             'type' : 'string'
43815                         },
43816                         {
43817                             'name' : 'dialCode',
43818                             'type' : 'string'
43819                         },
43820                         {
43821                             'name' : 'priority',
43822                             'type' : 'string'
43823                         },
43824                         {
43825                             'name' : 'areaCodes',
43826                             'type' : 'string'
43827                         }
43828                     ]
43829                 })
43830             });
43831             
43832             if(!this.preferedCountries) {
43833                 this.preferedCountries = [
43834                     'hk',
43835                     'gb',
43836                     'us'
43837                 ];
43838             }
43839             
43840             var p = this.preferedCountries.reverse();
43841             
43842             if(p) {
43843                 for (var i = 0; i < p.length; i++) {
43844                     for (var j = 0; j < this.allCountries.length; j++) {
43845                         if(this.allCountries[j].iso2 == p[i]) {
43846                             var t = this.allCountries[j];
43847                             this.allCountries.splice(j,1);
43848                             this.allCountries.unshift(t);
43849                         }
43850                     } 
43851                 }
43852             }
43853             
43854             this.store.proxy.data = {
43855                 success: true,
43856                 data: this.allCountries
43857             };
43858             
43859             return cfg;
43860         },
43861         
43862         initEvents : function()
43863         {
43864             this.createList();
43865             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43866             
43867             this.indicator = this.indicatorEl();
43868             this.flag = this.flagEl();
43869             this.dialCodeHolder = this.dialCodeHolderEl();
43870             
43871             this.trigger = this.el.select('div.flag-box',true).first();
43872             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43873             
43874             var _this = this;
43875             
43876             (function(){
43877                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43878                 _this.list.setWidth(lw);
43879             }).defer(100);
43880             
43881             this.list.on('mouseover', this.onViewOver, this);
43882             this.list.on('mousemove', this.onViewMove, this);
43883             this.inputEl().on("keyup", this.onKeyUp, this);
43884             this.inputEl().on("keypress", this.onKeyPress, this);
43885             
43886             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43887
43888             this.view = new Roo.View(this.list, this.tpl, {
43889                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43890             });
43891             
43892             this.view.on('click', this.onViewClick, this);
43893             this.setValue(this.defaultDialCode);
43894         },
43895         
43896         onTriggerClick : function(e)
43897         {
43898             Roo.log('trigger click');
43899             if(this.disabled){
43900                 return;
43901             }
43902             
43903             if(this.isExpanded()){
43904                 this.collapse();
43905                 this.hasFocus = false;
43906             }else {
43907                 this.store.load({});
43908                 this.hasFocus = true;
43909                 this.expand();
43910             }
43911         },
43912         
43913         isExpanded : function()
43914         {
43915             return this.list.isVisible();
43916         },
43917         
43918         collapse : function()
43919         {
43920             if(!this.isExpanded()){
43921                 return;
43922             }
43923             this.list.hide();
43924             Roo.get(document).un('mousedown', this.collapseIf, this);
43925             Roo.get(document).un('mousewheel', this.collapseIf, this);
43926             this.fireEvent('collapse', this);
43927             this.validate();
43928         },
43929         
43930         expand : function()
43931         {
43932             Roo.log('expand');
43933
43934             if(this.isExpanded() || !this.hasFocus){
43935                 return;
43936             }
43937             
43938             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43939             this.list.setWidth(lw);
43940             
43941             this.list.show();
43942             this.restrictHeight();
43943             
43944             Roo.get(document).on('mousedown', this.collapseIf, this);
43945             Roo.get(document).on('mousewheel', this.collapseIf, this);
43946             
43947             this.fireEvent('expand', this);
43948         },
43949         
43950         restrictHeight : function()
43951         {
43952             this.list.alignTo(this.inputEl(), this.listAlign);
43953             this.list.alignTo(this.inputEl(), this.listAlign);
43954         },
43955         
43956         onViewOver : function(e, t)
43957         {
43958             if(this.inKeyMode){
43959                 return;
43960             }
43961             var item = this.view.findItemFromChild(t);
43962             
43963             if(item){
43964                 var index = this.view.indexOf(item);
43965                 this.select(index, false);
43966             }
43967         },
43968
43969         // private
43970         onViewClick : function(view, doFocus, el, e)
43971         {
43972             var index = this.view.getSelectedIndexes()[0];
43973             
43974             var r = this.store.getAt(index);
43975             
43976             if(r){
43977                 this.onSelect(r, index);
43978             }
43979             if(doFocus !== false && !this.blockFocus){
43980                 this.inputEl().focus();
43981             }
43982         },
43983         
43984         onViewMove : function(e, t)
43985         {
43986             this.inKeyMode = false;
43987         },
43988         
43989         select : function(index, scrollIntoView)
43990         {
43991             this.selectedIndex = index;
43992             this.view.select(index);
43993             if(scrollIntoView !== false){
43994                 var el = this.view.getNode(index);
43995                 if(el){
43996                     this.list.scrollChildIntoView(el, false);
43997                 }
43998             }
43999         },
44000         
44001         createList : function()
44002         {
44003             this.list = Roo.get(document.body).createChild({
44004                 tag: 'ul',
44005                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44006                 style: 'display:none'
44007             });
44008             
44009             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44010         },
44011         
44012         collapseIf : function(e)
44013         {
44014             var in_combo  = e.within(this.el);
44015             var in_list =  e.within(this.list);
44016             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44017             
44018             if (in_combo || in_list || is_list) {
44019                 return;
44020             }
44021             this.collapse();
44022         },
44023         
44024         onSelect : function(record, index)
44025         {
44026             if(this.fireEvent('beforeselect', this, record, index) !== false){
44027                 
44028                 this.setFlagClass(record.data.iso2);
44029                 this.setDialCode(record.data.dialCode);
44030                 this.hasFocus = false;
44031                 this.collapse();
44032                 this.fireEvent('select', this, record, index);
44033             }
44034         },
44035         
44036         flagEl : function()
44037         {
44038             var flag = this.el.select('div.flag',true).first();
44039             if(!flag){
44040                 return false;
44041             }
44042             return flag;
44043         },
44044         
44045         dialCodeHolderEl : function()
44046         {
44047             var d = this.el.select('input.dial-code-holder',true).first();
44048             if(!d){
44049                 return false;
44050             }
44051             return d;
44052         },
44053         
44054         setDialCode : function(v)
44055         {
44056             this.dialCodeHolder.dom.value = '+'+v;
44057         },
44058         
44059         setFlagClass : function(n)
44060         {
44061             this.flag.dom.className = 'flag '+n;
44062         },
44063         
44064         getValue : function()
44065         {
44066             var v = this.inputEl().getValue();
44067             if(this.dialCodeHolder) {
44068                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44069             }
44070             return v;
44071         },
44072         
44073         setValue : function(v)
44074         {
44075             var d = this.getDialCode(v);
44076             
44077             //invalid dial code
44078             if(v.length == 0 || !d || d.length == 0) {
44079                 if(this.rendered){
44080                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44081                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44082                 }
44083                 return;
44084             }
44085             
44086             //valid dial code
44087             this.setFlagClass(this.dialCodeMapping[d].iso2);
44088             this.setDialCode(d);
44089             this.inputEl().dom.value = v.replace('+'+d,'');
44090             this.hiddenEl().dom.value = this.getValue();
44091             
44092             this.validate();
44093         },
44094         
44095         getDialCode : function(v)
44096         {
44097             v = v ||  '';
44098             
44099             if (v.length == 0) {
44100                 return this.dialCodeHolder.dom.value;
44101             }
44102             
44103             var dialCode = "";
44104             if (v.charAt(0) != "+") {
44105                 return false;
44106             }
44107             var numericChars = "";
44108             for (var i = 1; i < v.length; i++) {
44109               var c = v.charAt(i);
44110               if (!isNaN(c)) {
44111                 numericChars += c;
44112                 if (this.dialCodeMapping[numericChars]) {
44113                   dialCode = v.substr(1, i);
44114                 }
44115                 if (numericChars.length == 4) {
44116                   break;
44117                 }
44118               }
44119             }
44120             return dialCode;
44121         },
44122         
44123         reset : function()
44124         {
44125             this.setValue(this.defaultDialCode);
44126             this.validate();
44127         },
44128         
44129         hiddenEl : function()
44130         {
44131             return this.el.select('input.hidden-tel-input',true).first();
44132         },
44133         
44134         // after setting val
44135         onKeyUp : function(e){
44136             this.setValue(this.getValue());
44137         },
44138         
44139         onKeyPress : function(e){
44140             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44141                 e.stopEvent();
44142             }
44143         }
44144         
44145 });
44146 /**
44147  * @class Roo.bootstrap.MoneyField
44148  * @extends Roo.bootstrap.ComboBox
44149  * Bootstrap MoneyField class
44150  * 
44151  * @constructor
44152  * Create a new MoneyField.
44153  * @param {Object} config Configuration options
44154  */
44155
44156 Roo.bootstrap.MoneyField = function(config) {
44157     
44158     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44159     
44160 };
44161
44162 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44163     
44164     /**
44165      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44166      */
44167     allowDecimals : true,
44168     /**
44169      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44170      */
44171     decimalSeparator : ".",
44172     /**
44173      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44174      */
44175     decimalPrecision : 0,
44176     /**
44177      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44178      */
44179     allowNegative : true,
44180     /**
44181      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44182      */
44183     allowZero: true,
44184     /**
44185      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44186      */
44187     minValue : Number.NEGATIVE_INFINITY,
44188     /**
44189      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44190      */
44191     maxValue : Number.MAX_VALUE,
44192     /**
44193      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44194      */
44195     minText : "The minimum value for this field is {0}",
44196     /**
44197      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44198      */
44199     maxText : "The maximum value for this field is {0}",
44200     /**
44201      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44202      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44203      */
44204     nanText : "{0} is not a valid number",
44205     /**
44206      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44207      */
44208     castInt : true,
44209     /**
44210      * @cfg {String} defaults currency of the MoneyField
44211      * value should be in lkey
44212      */
44213     defaultCurrency : false,
44214     /**
44215      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44216      */
44217     thousandsDelimiter : false,
44218     /**
44219      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44220      */
44221     max_length: false,
44222     
44223     inputlg : 9,
44224     inputmd : 9,
44225     inputsm : 9,
44226     inputxs : 6,
44227     
44228     store : false,
44229     
44230     getAutoCreate : function()
44231     {
44232         var align = this.labelAlign || this.parentLabelAlign();
44233         
44234         var id = Roo.id();
44235
44236         var cfg = {
44237             cls: 'form-group',
44238             cn: []
44239         };
44240
44241         var input =  {
44242             tag: 'input',
44243             id : id,
44244             cls : 'form-control roo-money-amount-input',
44245             autocomplete: 'new-password'
44246         };
44247         
44248         var hiddenInput = {
44249             tag: 'input',
44250             type: 'hidden',
44251             id: Roo.id(),
44252             cls: 'hidden-number-input'
44253         };
44254         
44255         if(this.max_length) {
44256             input.maxlength = this.max_length; 
44257         }
44258         
44259         if (this.name) {
44260             hiddenInput.name = this.name;
44261         }
44262
44263         if (this.disabled) {
44264             input.disabled = true;
44265         }
44266
44267         var clg = 12 - this.inputlg;
44268         var cmd = 12 - this.inputmd;
44269         var csm = 12 - this.inputsm;
44270         var cxs = 12 - this.inputxs;
44271         
44272         var container = {
44273             tag : 'div',
44274             cls : 'row roo-money-field',
44275             cn : [
44276                 {
44277                     tag : 'div',
44278                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44279                     cn : [
44280                         {
44281                             tag : 'div',
44282                             cls: 'roo-select2-container input-group',
44283                             cn: [
44284                                 {
44285                                     tag : 'input',
44286                                     cls : 'form-control roo-money-currency-input',
44287                                     autocomplete: 'new-password',
44288                                     readOnly : 1,
44289                                     name : this.currencyName
44290                                 },
44291                                 {
44292                                     tag :'span',
44293                                     cls : 'input-group-addon',
44294                                     cn : [
44295                                         {
44296                                             tag: 'span',
44297                                             cls: 'caret'
44298                                         }
44299                                     ]
44300                                 }
44301                             ]
44302                         }
44303                     ]
44304                 },
44305                 {
44306                     tag : 'div',
44307                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44308                     cn : [
44309                         {
44310                             tag: 'div',
44311                             cls: this.hasFeedback ? 'has-feedback' : '',
44312                             cn: [
44313                                 input
44314                             ]
44315                         }
44316                     ]
44317                 }
44318             ]
44319             
44320         };
44321         
44322         if (this.fieldLabel.length) {
44323             var indicator = {
44324                 tag: 'i',
44325                 tooltip: 'This field is required'
44326             };
44327
44328             var label = {
44329                 tag: 'label',
44330                 'for':  id,
44331                 cls: 'control-label',
44332                 cn: []
44333             };
44334
44335             var label_text = {
44336                 tag: 'span',
44337                 html: this.fieldLabel
44338             };
44339
44340             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44341             label.cn = [
44342                 indicator,
44343                 label_text
44344             ];
44345
44346             if(this.indicatorpos == 'right') {
44347                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44348                 label.cn = [
44349                     label_text,
44350                     indicator
44351                 ];
44352             }
44353
44354             if(align == 'left') {
44355                 container = {
44356                     tag: 'div',
44357                     cn: [
44358                         container
44359                     ]
44360                 };
44361
44362                 if(this.labelWidth > 12){
44363                     label.style = "width: " + this.labelWidth + 'px';
44364                 }
44365                 if(this.labelWidth < 13 && this.labelmd == 0){
44366                     this.labelmd = this.labelWidth;
44367                 }
44368                 if(this.labellg > 0){
44369                     label.cls += ' col-lg-' + this.labellg;
44370                     input.cls += ' col-lg-' + (12 - this.labellg);
44371                 }
44372                 if(this.labelmd > 0){
44373                     label.cls += ' col-md-' + this.labelmd;
44374                     container.cls += ' col-md-' + (12 - this.labelmd);
44375                 }
44376                 if(this.labelsm > 0){
44377                     label.cls += ' col-sm-' + this.labelsm;
44378                     container.cls += ' col-sm-' + (12 - this.labelsm);
44379                 }
44380                 if(this.labelxs > 0){
44381                     label.cls += ' col-xs-' + this.labelxs;
44382                     container.cls += ' col-xs-' + (12 - this.labelxs);
44383                 }
44384             }
44385         }
44386
44387         cfg.cn = [
44388             label,
44389             container,
44390             hiddenInput
44391         ];
44392         
44393         var settings = this;
44394
44395         ['xs','sm','md','lg'].map(function(size){
44396             if (settings[size]) {
44397                 cfg.cls += ' col-' + size + '-' + settings[size];
44398             }
44399         });
44400         
44401         return cfg;
44402     },
44403     
44404     initEvents : function()
44405     {
44406         this.indicator = this.indicatorEl();
44407         
44408         this.initCurrencyEvent();
44409         
44410         this.initNumberEvent();
44411     },
44412     
44413     initCurrencyEvent : function()
44414     {
44415         if (!this.store) {
44416             throw "can not find store for combo";
44417         }
44418         
44419         this.store = Roo.factory(this.store, Roo.data);
44420         this.store.parent = this;
44421         
44422         this.createList();
44423         
44424         this.triggerEl = this.el.select('.input-group-addon', true).first();
44425         
44426         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44427         
44428         var _this = this;
44429         
44430         (function(){
44431             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44432             _this.list.setWidth(lw);
44433         }).defer(100);
44434         
44435         this.list.on('mouseover', this.onViewOver, this);
44436         this.list.on('mousemove', this.onViewMove, this);
44437         this.list.on('scroll', this.onViewScroll, this);
44438         
44439         if(!this.tpl){
44440             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44441         }
44442         
44443         this.view = new Roo.View(this.list, this.tpl, {
44444             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44445         });
44446         
44447         this.view.on('click', this.onViewClick, this);
44448         
44449         this.store.on('beforeload', this.onBeforeLoad, this);
44450         this.store.on('load', this.onLoad, this);
44451         this.store.on('loadexception', this.onLoadException, this);
44452         
44453         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44454             "up" : function(e){
44455                 this.inKeyMode = true;
44456                 this.selectPrev();
44457             },
44458
44459             "down" : function(e){
44460                 if(!this.isExpanded()){
44461                     this.onTriggerClick();
44462                 }else{
44463                     this.inKeyMode = true;
44464                     this.selectNext();
44465                 }
44466             },
44467
44468             "enter" : function(e){
44469                 this.collapse();
44470                 
44471                 if(this.fireEvent("specialkey", this, e)){
44472                     this.onViewClick(false);
44473                 }
44474                 
44475                 return true;
44476             },
44477
44478             "esc" : function(e){
44479                 this.collapse();
44480             },
44481
44482             "tab" : function(e){
44483                 this.collapse();
44484                 
44485                 if(this.fireEvent("specialkey", this, e)){
44486                     this.onViewClick(false);
44487                 }
44488                 
44489                 return true;
44490             },
44491
44492             scope : this,
44493
44494             doRelay : function(foo, bar, hname){
44495                 if(hname == 'down' || this.scope.isExpanded()){
44496                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44497                 }
44498                 return true;
44499             },
44500
44501             forceKeyDown: true
44502         });
44503         
44504         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44505         
44506     },
44507     
44508     initNumberEvent : function(e)
44509     {
44510         this.inputEl().on("keydown" , this.fireKey,  this);
44511         this.inputEl().on("focus", this.onFocus,  this);
44512         this.inputEl().on("blur", this.onBlur,  this);
44513         
44514         this.inputEl().relayEvent('keyup', this);
44515         
44516         if(this.indicator){
44517             this.indicator.addClass('invisible');
44518         }
44519  
44520         this.originalValue = this.getValue();
44521         
44522         if(this.validationEvent == 'keyup'){
44523             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44524             this.inputEl().on('keyup', this.filterValidation, this);
44525         }
44526         else if(this.validationEvent !== false){
44527             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44528         }
44529         
44530         if(this.selectOnFocus){
44531             this.on("focus", this.preFocus, this);
44532             
44533         }
44534         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44535             this.inputEl().on("keypress", this.filterKeys, this);
44536         } else {
44537             this.inputEl().relayEvent('keypress', this);
44538         }
44539         
44540         var allowed = "0123456789";
44541         
44542         if(this.allowDecimals){
44543             allowed += this.decimalSeparator;
44544         }
44545         
44546         if(this.allowNegative){
44547             allowed += "-";
44548         }
44549         
44550         if(this.thousandsDelimiter) {
44551             allowed += ",";
44552         }
44553         
44554         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44555         
44556         var keyPress = function(e){
44557             
44558             var k = e.getKey();
44559             
44560             var c = e.getCharCode();
44561             
44562             if(
44563                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44564                     allowed.indexOf(String.fromCharCode(c)) === -1
44565             ){
44566                 e.stopEvent();
44567                 return;
44568             }
44569             
44570             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44571                 return;
44572             }
44573             
44574             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44575                 e.stopEvent();
44576             }
44577         };
44578         
44579         this.inputEl().on("keypress", keyPress, this);
44580         
44581     },
44582     
44583     onTriggerClick : function(e)
44584     {   
44585         if(this.disabled){
44586             return;
44587         }
44588         
44589         this.page = 0;
44590         this.loadNext = false;
44591         
44592         if(this.isExpanded()){
44593             this.collapse();
44594             return;
44595         }
44596         
44597         this.hasFocus = true;
44598         
44599         if(this.triggerAction == 'all') {
44600             this.doQuery(this.allQuery, true);
44601             return;
44602         }
44603         
44604         this.doQuery(this.getRawValue());
44605     },
44606     
44607     getCurrency : function()
44608     {   
44609         var v = this.currencyEl().getValue();
44610         
44611         return v;
44612     },
44613     
44614     restrictHeight : function()
44615     {
44616         this.list.alignTo(this.currencyEl(), this.listAlign);
44617         this.list.alignTo(this.currencyEl(), this.listAlign);
44618     },
44619     
44620     onViewClick : function(view, doFocus, el, e)
44621     {
44622         var index = this.view.getSelectedIndexes()[0];
44623         
44624         var r = this.store.getAt(index);
44625         
44626         if(r){
44627             this.onSelect(r, index);
44628         }
44629     },
44630     
44631     onSelect : function(record, index){
44632         
44633         if(this.fireEvent('beforeselect', this, record, index) !== false){
44634         
44635             this.setFromCurrencyData(index > -1 ? record.data : false);
44636             
44637             this.collapse();
44638             
44639             this.fireEvent('select', this, record, index);
44640         }
44641     },
44642     
44643     setFromCurrencyData : function(o)
44644     {
44645         var currency = '';
44646         
44647         this.lastCurrency = o;
44648         
44649         if (this.currencyField) {
44650             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44651         } else {
44652             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44653         }
44654         
44655         this.lastSelectionText = currency;
44656         
44657         //setting default currency
44658         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44659             this.setCurrency(this.defaultCurrency);
44660             return;
44661         }
44662         
44663         this.setCurrency(currency);
44664     },
44665     
44666     setFromData : function(o)
44667     {
44668         var c = {};
44669         
44670         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44671         
44672         this.setFromCurrencyData(c);
44673         
44674         var value = '';
44675         
44676         if (this.name) {
44677             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44678         } else {
44679             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44680         }
44681         
44682         this.setValue(value);
44683         
44684     },
44685     
44686     setCurrency : function(v)
44687     {   
44688         this.currencyValue = v;
44689         
44690         if(this.rendered){
44691             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44692             this.validate();
44693         }
44694     },
44695     
44696     setValue : function(v)
44697     {
44698         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44699         
44700         this.value = v;
44701         
44702         if(this.rendered){
44703             
44704             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44705             
44706             this.inputEl().dom.value = (v == '') ? '' :
44707                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44708             
44709             if(!this.allowZero && v === '0') {
44710                 this.hiddenEl().dom.value = '';
44711                 this.inputEl().dom.value = '';
44712             }
44713             
44714             this.validate();
44715         }
44716     },
44717     
44718     getRawValue : function()
44719     {
44720         var v = this.inputEl().getValue();
44721         
44722         return v;
44723     },
44724     
44725     getValue : function()
44726     {
44727         return this.fixPrecision(this.parseValue(this.getRawValue()));
44728     },
44729     
44730     parseValue : function(value)
44731     {
44732         if(this.thousandsDelimiter) {
44733             value += "";
44734             r = new RegExp(",", "g");
44735             value = value.replace(r, "");
44736         }
44737         
44738         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44739         return isNaN(value) ? '' : value;
44740         
44741     },
44742     
44743     fixPrecision : function(value)
44744     {
44745         if(this.thousandsDelimiter) {
44746             value += "";
44747             r = new RegExp(",", "g");
44748             value = value.replace(r, "");
44749         }
44750         
44751         var nan = isNaN(value);
44752         
44753         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44754             return nan ? '' : value;
44755         }
44756         return parseFloat(value).toFixed(this.decimalPrecision);
44757     },
44758     
44759     decimalPrecisionFcn : function(v)
44760     {
44761         return Math.floor(v);
44762     },
44763     
44764     validateValue : function(value)
44765     {
44766         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44767             return false;
44768         }
44769         
44770         var num = this.parseValue(value);
44771         
44772         if(isNaN(num)){
44773             this.markInvalid(String.format(this.nanText, value));
44774             return false;
44775         }
44776         
44777         if(num < this.minValue){
44778             this.markInvalid(String.format(this.minText, this.minValue));
44779             return false;
44780         }
44781         
44782         if(num > this.maxValue){
44783             this.markInvalid(String.format(this.maxText, this.maxValue));
44784             return false;
44785         }
44786         
44787         return true;
44788     },
44789     
44790     validate : function()
44791     {
44792         if(this.disabled || this.allowBlank){
44793             this.markValid();
44794             return true;
44795         }
44796         
44797         var currency = this.getCurrency();
44798         
44799         if(this.validateValue(this.getRawValue()) && currency.length){
44800             this.markValid();
44801             return true;
44802         }
44803         
44804         this.markInvalid();
44805         return false;
44806     },
44807     
44808     getName: function()
44809     {
44810         return this.name;
44811     },
44812     
44813     beforeBlur : function()
44814     {
44815         if(!this.castInt){
44816             return;
44817         }
44818         
44819         var v = this.parseValue(this.getRawValue());
44820         
44821         if(v || v == 0){
44822             this.setValue(v);
44823         }
44824     },
44825     
44826     onBlur : function()
44827     {
44828         this.beforeBlur();
44829         
44830         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44831             //this.el.removeClass(this.focusClass);
44832         }
44833         
44834         this.hasFocus = false;
44835         
44836         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44837             this.validate();
44838         }
44839         
44840         var v = this.getValue();
44841         
44842         if(String(v) !== String(this.startValue)){
44843             this.fireEvent('change', this, v, this.startValue);
44844         }
44845         
44846         this.fireEvent("blur", this);
44847     },
44848     
44849     inputEl : function()
44850     {
44851         return this.el.select('.roo-money-amount-input', true).first();
44852     },
44853     
44854     currencyEl : function()
44855     {
44856         return this.el.select('.roo-money-currency-input', true).first();
44857     },
44858     
44859     hiddenEl : function()
44860     {
44861         return this.el.select('input.hidden-number-input',true).first();
44862     }
44863     
44864 });/**
44865  * @class Roo.bootstrap.BezierSignature
44866  * @extends Roo.bootstrap.Component
44867  * Bootstrap BezierSignature class
44868  * This script refer to:
44869  *    Title: Signature Pad
44870  *    Author: szimek
44871  *    Availability: https://github.com/szimek/signature_pad
44872  *
44873  * @constructor
44874  * Create a new BezierSignature
44875  * @param {Object} config The config object
44876  */
44877
44878 Roo.bootstrap.BezierSignature = function(config){
44879     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44880     this.addEvents({
44881         "resize" : true
44882     });
44883 };
44884
44885 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44886 {
44887      
44888     curve_data: [],
44889     
44890     is_empty: true,
44891     
44892     mouse_btn_down: true,
44893     
44894     /**
44895      * @cfg {int} canvas height
44896      */
44897     canvas_height: '200px',
44898     
44899     /**
44900      * @cfg {float|function} Radius of a single dot.
44901      */ 
44902     dot_size: false,
44903     
44904     /**
44905      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44906      */
44907     min_width: 0.5,
44908     
44909     /**
44910      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44911      */
44912     max_width: 2.5,
44913     
44914     /**
44915      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44916      */
44917     throttle: 16,
44918     
44919     /**
44920      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44921      */
44922     min_distance: 5,
44923     
44924     /**
44925      * @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.
44926      */
44927     bg_color: 'rgba(0, 0, 0, 0)',
44928     
44929     /**
44930      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44931      */
44932     dot_color: 'black',
44933     
44934     /**
44935      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44936      */ 
44937     velocity_filter_weight: 0.7,
44938     
44939     /**
44940      * @cfg {function} Callback when stroke begin. 
44941      */
44942     onBegin: false,
44943     
44944     /**
44945      * @cfg {function} Callback when stroke end.
44946      */
44947     onEnd: false,
44948     
44949     getAutoCreate : function()
44950     {
44951         var cls = 'roo-signature column';
44952         
44953         if(this.cls){
44954             cls += ' ' + this.cls;
44955         }
44956         
44957         var col_sizes = [
44958             'lg',
44959             'md',
44960             'sm',
44961             'xs'
44962         ];
44963         
44964         for(var i = 0; i < col_sizes.length; i++) {
44965             if(this[col_sizes[i]]) {
44966                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44967             }
44968         }
44969         
44970         var cfg = {
44971             tag: 'div',
44972             cls: cls,
44973             cn: [
44974                 {
44975                     tag: 'div',
44976                     cls: 'roo-signature-body',
44977                     cn: [
44978                         {
44979                             tag: 'canvas',
44980                             cls: 'roo-signature-body-canvas',
44981                             height: this.canvas_height,
44982                             width: this.canvas_width
44983                         }
44984                     ]
44985                 },
44986                 {
44987                     tag: 'input',
44988                     type: 'file',
44989                     style: 'display: none'
44990                 }
44991             ]
44992         };
44993         
44994         return cfg;
44995     },
44996     
44997     initEvents: function() 
44998     {
44999         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45000         
45001         var canvas = this.canvasEl();
45002         
45003         // mouse && touch event swapping...
45004         canvas.dom.style.touchAction = 'none';
45005         canvas.dom.style.msTouchAction = 'none';
45006         
45007         this.mouse_btn_down = false;
45008         canvas.on('mousedown', this._handleMouseDown, this);
45009         canvas.on('mousemove', this._handleMouseMove, this);
45010         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45011         
45012         if (window.PointerEvent) {
45013             canvas.on('pointerdown', this._handleMouseDown, this);
45014             canvas.on('pointermove', this._handleMouseMove, this);
45015             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45016         }
45017         
45018         if ('ontouchstart' in window) {
45019             canvas.on('touchstart', this._handleTouchStart, this);
45020             canvas.on('touchmove', this._handleTouchMove, this);
45021             canvas.on('touchend', this._handleTouchEnd, this);
45022         }
45023         
45024         Roo.EventManager.onWindowResize(this.resize, this, true);
45025         
45026         // file input event
45027         this.fileEl().on('change', this.uploadImage, this);
45028         
45029         this.clear();
45030         
45031         this.resize();
45032     },
45033     
45034     resize: function(){
45035         
45036         var canvas = this.canvasEl().dom;
45037         var ctx = this.canvasElCtx();
45038         var img_data = false;
45039         
45040         if(canvas.width > 0) {
45041             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45042         }
45043         // setting canvas width will clean img data
45044         canvas.width = 0;
45045         
45046         var style = window.getComputedStyle ? 
45047             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45048             
45049         var padding_left = parseInt(style.paddingLeft) || 0;
45050         var padding_right = parseInt(style.paddingRight) || 0;
45051         
45052         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45053         
45054         if(img_data) {
45055             ctx.putImageData(img_data, 0, 0);
45056         }
45057     },
45058     
45059     _handleMouseDown: function(e)
45060     {
45061         if (e.browserEvent.which === 1) {
45062             this.mouse_btn_down = true;
45063             this.strokeBegin(e);
45064         }
45065     },
45066     
45067     _handleMouseMove: function (e)
45068     {
45069         if (this.mouse_btn_down) {
45070             this.strokeMoveUpdate(e);
45071         }
45072     },
45073     
45074     _handleMouseUp: function (e)
45075     {
45076         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45077             this.mouse_btn_down = false;
45078             this.strokeEnd(e);
45079         }
45080     },
45081     
45082     _handleTouchStart: function (e) {
45083         
45084         e.preventDefault();
45085         if (e.browserEvent.targetTouches.length === 1) {
45086             // var touch = e.browserEvent.changedTouches[0];
45087             // this.strokeBegin(touch);
45088             
45089              this.strokeBegin(e); // assume e catching the correct xy...
45090         }
45091     },
45092     
45093     _handleTouchMove: function (e) {
45094         e.preventDefault();
45095         // var touch = event.targetTouches[0];
45096         // _this._strokeMoveUpdate(touch);
45097         this.strokeMoveUpdate(e);
45098     },
45099     
45100     _handleTouchEnd: function (e) {
45101         var wasCanvasTouched = e.target === this.canvasEl().dom;
45102         if (wasCanvasTouched) {
45103             e.preventDefault();
45104             // var touch = event.changedTouches[0];
45105             // _this._strokeEnd(touch);
45106             this.strokeEnd(e);
45107         }
45108     },
45109     
45110     reset: function () {
45111         this._lastPoints = [];
45112         this._lastVelocity = 0;
45113         this._lastWidth = (this.min_width + this.max_width) / 2;
45114         this.canvasElCtx().fillStyle = this.dot_color;
45115     },
45116     
45117     strokeMoveUpdate: function(e)
45118     {
45119         this.strokeUpdate(e);
45120         
45121         if (this.throttle) {
45122             this.throttleStroke(this.strokeUpdate, this.throttle);
45123         }
45124         else {
45125             this.strokeUpdate(e);
45126         }
45127     },
45128     
45129     strokeBegin: function(e)
45130     {
45131         var newPointGroup = {
45132             color: this.dot_color,
45133             points: []
45134         };
45135         
45136         if (typeof this.onBegin === 'function') {
45137             this.onBegin(e);
45138         }
45139         
45140         this.curve_data.push(newPointGroup);
45141         this.reset();
45142         this.strokeUpdate(e);
45143     },
45144     
45145     strokeUpdate: function(e)
45146     {
45147         var rect = this.canvasEl().dom.getBoundingClientRect();
45148         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45149         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45150         var lastPoints = lastPointGroup.points;
45151         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45152         var isLastPointTooClose = lastPoint
45153             ? point.distanceTo(lastPoint) <= this.min_distance
45154             : false;
45155         var color = lastPointGroup.color;
45156         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45157             var curve = this.addPoint(point);
45158             if (!lastPoint) {
45159                 this.drawDot({color: color, point: point});
45160             }
45161             else if (curve) {
45162                 this.drawCurve({color: color, curve: curve});
45163             }
45164             lastPoints.push({
45165                 time: point.time,
45166                 x: point.x,
45167                 y: point.y
45168             });
45169         }
45170     },
45171     
45172     strokeEnd: function(e)
45173     {
45174         this.strokeUpdate(e);
45175         if (typeof this.onEnd === 'function') {
45176             this.onEnd(e);
45177         }
45178     },
45179     
45180     addPoint:  function (point) {
45181         var _lastPoints = this._lastPoints;
45182         _lastPoints.push(point);
45183         if (_lastPoints.length > 2) {
45184             if (_lastPoints.length === 3) {
45185                 _lastPoints.unshift(_lastPoints[0]);
45186             }
45187             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45188             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45189             _lastPoints.shift();
45190             return curve;
45191         }
45192         return null;
45193     },
45194     
45195     calculateCurveWidths: function (startPoint, endPoint) {
45196         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45197             (1 - this.velocity_filter_weight) * this._lastVelocity;
45198
45199         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45200         var widths = {
45201             end: newWidth,
45202             start: this._lastWidth
45203         };
45204         
45205         this._lastVelocity = velocity;
45206         this._lastWidth = newWidth;
45207         return widths;
45208     },
45209     
45210     drawDot: function (_a) {
45211         var color = _a.color, point = _a.point;
45212         var ctx = this.canvasElCtx();
45213         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45214         ctx.beginPath();
45215         this.drawCurveSegment(point.x, point.y, width);
45216         ctx.closePath();
45217         ctx.fillStyle = color;
45218         ctx.fill();
45219     },
45220     
45221     drawCurve: function (_a) {
45222         var color = _a.color, curve = _a.curve;
45223         var ctx = this.canvasElCtx();
45224         var widthDelta = curve.endWidth - curve.startWidth;
45225         var drawSteps = Math.floor(curve.length()) * 2;
45226         ctx.beginPath();
45227         ctx.fillStyle = color;
45228         for (var i = 0; i < drawSteps; i += 1) {
45229         var t = i / drawSteps;
45230         var tt = t * t;
45231         var ttt = tt * t;
45232         var u = 1 - t;
45233         var uu = u * u;
45234         var uuu = uu * u;
45235         var x = uuu * curve.startPoint.x;
45236         x += 3 * uu * t * curve.control1.x;
45237         x += 3 * u * tt * curve.control2.x;
45238         x += ttt * curve.endPoint.x;
45239         var y = uuu * curve.startPoint.y;
45240         y += 3 * uu * t * curve.control1.y;
45241         y += 3 * u * tt * curve.control2.y;
45242         y += ttt * curve.endPoint.y;
45243         var width = curve.startWidth + ttt * widthDelta;
45244         this.drawCurveSegment(x, y, width);
45245         }
45246         ctx.closePath();
45247         ctx.fill();
45248     },
45249     
45250     drawCurveSegment: function (x, y, width) {
45251         var ctx = this.canvasElCtx();
45252         ctx.moveTo(x, y);
45253         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45254         this.is_empty = false;
45255     },
45256     
45257     clear: function()
45258     {
45259         var ctx = this.canvasElCtx();
45260         var canvas = this.canvasEl().dom;
45261         ctx.fillStyle = this.bg_color;
45262         ctx.clearRect(0, 0, canvas.width, canvas.height);
45263         ctx.fillRect(0, 0, canvas.width, canvas.height);
45264         this.curve_data = [];
45265         this.reset();
45266         this.is_empty = true;
45267     },
45268     
45269     fileEl: function()
45270     {
45271         return  this.el.select('input',true).first();
45272     },
45273     
45274     canvasEl: function()
45275     {
45276         return this.el.select('canvas',true).first();
45277     },
45278     
45279     canvasElCtx: function()
45280     {
45281         return this.el.select('canvas',true).first().dom.getContext('2d');
45282     },
45283     
45284     getImage: function(type)
45285     {
45286         if(this.is_empty) {
45287             return false;
45288         }
45289         
45290         // encryption ?
45291         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45292     },
45293     
45294     drawFromImage: function(img_src)
45295     {
45296         var img = new Image();
45297         
45298         img.onload = function(){
45299             this.canvasElCtx().drawImage(img, 0, 0);
45300         }.bind(this);
45301         
45302         img.src = img_src;
45303         
45304         this.is_empty = false;
45305     },
45306     
45307     selectImage: function()
45308     {
45309         this.fileEl().dom.click();
45310     },
45311     
45312     uploadImage: function(e)
45313     {
45314         var reader = new FileReader();
45315         
45316         reader.onload = function(e){
45317             var img = new Image();
45318             img.onload = function(){
45319                 this.reset();
45320                 this.canvasElCtx().drawImage(img, 0, 0);
45321             }.bind(this);
45322             img.src = e.target.result;
45323         }.bind(this);
45324         
45325         reader.readAsDataURL(e.target.files[0]);
45326     },
45327     
45328     // Bezier Point Constructor
45329     Point: (function () {
45330         function Point(x, y, time) {
45331             this.x = x;
45332             this.y = y;
45333             this.time = time || Date.now();
45334         }
45335         Point.prototype.distanceTo = function (start) {
45336             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45337         };
45338         Point.prototype.equals = function (other) {
45339             return this.x === other.x && this.y === other.y && this.time === other.time;
45340         };
45341         Point.prototype.velocityFrom = function (start) {
45342             return this.time !== start.time
45343             ? this.distanceTo(start) / (this.time - start.time)
45344             : 0;
45345         };
45346         return Point;
45347     }()),
45348     
45349     
45350     // Bezier Constructor
45351     Bezier: (function () {
45352         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45353             this.startPoint = startPoint;
45354             this.control2 = control2;
45355             this.control1 = control1;
45356             this.endPoint = endPoint;
45357             this.startWidth = startWidth;
45358             this.endWidth = endWidth;
45359         }
45360         Bezier.fromPoints = function (points, widths, scope) {
45361             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45362             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45363             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45364         };
45365         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45366             var dx1 = s1.x - s2.x;
45367             var dy1 = s1.y - s2.y;
45368             var dx2 = s2.x - s3.x;
45369             var dy2 = s2.y - s3.y;
45370             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45371             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45372             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45373             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45374             var dxm = m1.x - m2.x;
45375             var dym = m1.y - m2.y;
45376             var k = l2 / (l1 + l2);
45377             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45378             var tx = s2.x - cm.x;
45379             var ty = s2.y - cm.y;
45380             return {
45381                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45382                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45383             };
45384         };
45385         Bezier.prototype.length = function () {
45386             var steps = 10;
45387             var length = 0;
45388             var px;
45389             var py;
45390             for (var i = 0; i <= steps; i += 1) {
45391                 var t = i / steps;
45392                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45393                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45394                 if (i > 0) {
45395                     var xdiff = cx - px;
45396                     var ydiff = cy - py;
45397                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45398                 }
45399                 px = cx;
45400                 py = cy;
45401             }
45402             return length;
45403         };
45404         Bezier.prototype.point = function (t, start, c1, c2, end) {
45405             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45406             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45407             + (3.0 * c2 * (1.0 - t) * t * t)
45408             + (end * t * t * t);
45409         };
45410         return Bezier;
45411     }()),
45412     
45413     throttleStroke: function(fn, wait) {
45414       if (wait === void 0) { wait = 250; }
45415       var previous = 0;
45416       var timeout = null;
45417       var result;
45418       var storedContext;
45419       var storedArgs;
45420       var later = function () {
45421           previous = Date.now();
45422           timeout = null;
45423           result = fn.apply(storedContext, storedArgs);
45424           if (!timeout) {
45425               storedContext = null;
45426               storedArgs = [];
45427           }
45428       };
45429       return function wrapper() {
45430           var args = [];
45431           for (var _i = 0; _i < arguments.length; _i++) {
45432               args[_i] = arguments[_i];
45433           }
45434           var now = Date.now();
45435           var remaining = wait - (now - previous);
45436           storedContext = this;
45437           storedArgs = args;
45438           if (remaining <= 0 || remaining > wait) {
45439               if (timeout) {
45440                   clearTimeout(timeout);
45441                   timeout = null;
45442               }
45443               previous = now;
45444               result = fn.apply(storedContext, storedArgs);
45445               if (!timeout) {
45446                   storedContext = null;
45447                   storedArgs = [];
45448               }
45449           }
45450           else if (!timeout) {
45451               timeout = window.setTimeout(later, remaining);
45452           }
45453           return result;
45454       };
45455   }
45456   
45457 });
45458
45459  
45460
45461