roojs-bootstrap.js
[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             if (this.responsive) {
9457                  
9458             
9459                 ['xs','sm','md','lg'].map(function(size){
9460                     
9461                     if(typeof(config[size]) == 'undefined'){
9462                         return;
9463                     }
9464                      
9465                     if (!config[size]) { // 0 = hidden
9466                         // BS 4 '0' is treated as hide that column and below.
9467                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9468                         return;
9469                     }
9470                     
9471                     c.cls += ' col-' + size + '-' + config[size] + (
9472                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9473                     );
9474                     
9475                     
9476                 });
9477             }
9478             // at the end?
9479             
9480             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9481             
9482             
9483             
9484             
9485             header.cn.push(c)
9486         }
9487         
9488         return header;
9489     },
9490     
9491     renderBody : function()
9492     {
9493         var body = {
9494             tag: 'tbody',
9495             cn : [
9496                 {
9497                     tag: 'tr',
9498                     cn : [
9499                         {
9500                             tag : 'td',
9501                             colspan :  this.cm.getColumnCount()
9502                         }
9503                     ]
9504                 }
9505             ]
9506         };
9507         
9508         return body;
9509     },
9510     
9511     renderFooter : function()
9512     {
9513         var footer = {
9514             tag: 'tfoot',
9515             cn : [
9516                 {
9517                     tag: 'tr',
9518                     cn : [
9519                         {
9520                             tag : 'td',
9521                             colspan :  this.cm.getColumnCount()
9522                         }
9523                     ]
9524                 }
9525             ]
9526         };
9527         
9528         return footer;
9529     },
9530     
9531     
9532     
9533     onLoad : function()
9534     {
9535 //        Roo.log('ds onload');
9536         this.clear();
9537         
9538         var _this = this;
9539         var cm = this.cm;
9540         var ds = this.store;
9541         
9542         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9543             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9544             if (_this.store.sortInfo) {
9545                     
9546                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9547                     e.select('i', true).addClass(['fa-arrow-up']);
9548                 }
9549                 
9550                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9551                     e.select('i', true).addClass(['fa-arrow-down']);
9552                 }
9553             }
9554         });
9555         
9556         var tbody =  this.bodyEl;
9557               
9558         if(ds.getCount() > 0){
9559             ds.data.each(function(d,rowIndex){
9560                 var row =  this.renderRow(cm, ds, rowIndex);
9561                 
9562                 tbody.createChild(row);
9563                 
9564                 var _this = this;
9565                 
9566                 if(row.cellObjects.length){
9567                     Roo.each(row.cellObjects, function(r){
9568                         _this.renderCellObject(r);
9569                     })
9570                 }
9571                 
9572             }, this);
9573         }
9574         
9575         var tfoot = this.el.select('tfoot', true).first();
9576         
9577         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9578             
9579             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9580             
9581             var total = this.ds.getTotalCount();
9582             
9583             if(this.footer.pageSize < total){
9584                 this.mainFoot.show();
9585             }
9586         }
9587         
9588         Roo.each(this.el.select('tbody td', true).elements, function(e){
9589             e.on('mouseover', _this.onMouseover, _this);
9590         });
9591         
9592         Roo.each(this.el.select('tbody td', true).elements, function(e){
9593             e.on('mouseout', _this.onMouseout, _this);
9594         });
9595         this.fireEvent('rowsrendered', this);
9596         
9597         this.autoSize();
9598         
9599         this.initCSS(); /// resize cols
9600
9601         
9602     },
9603     
9604     
9605     onUpdate : function(ds,record)
9606     {
9607         this.refreshRow(record);
9608         this.autoSize();
9609     },
9610     
9611     onRemove : function(ds, record, index, isUpdate){
9612         if(isUpdate !== true){
9613             this.fireEvent("beforerowremoved", this, index, record);
9614         }
9615         var bt = this.bodyEl.dom;
9616         
9617         var rows = this.el.select('tbody > tr', true).elements;
9618         
9619         if(typeof(rows[index]) != 'undefined'){
9620             bt.removeChild(rows[index].dom);
9621         }
9622         
9623 //        if(bt.rows[index]){
9624 //            bt.removeChild(bt.rows[index]);
9625 //        }
9626         
9627         if(isUpdate !== true){
9628             //this.stripeRows(index);
9629             //this.syncRowHeights(index, index);
9630             //this.layout();
9631             this.fireEvent("rowremoved", this, index, record);
9632         }
9633     },
9634     
9635     onAdd : function(ds, records, rowIndex)
9636     {
9637         //Roo.log('on Add called');
9638         // - note this does not handle multiple adding very well..
9639         var bt = this.bodyEl.dom;
9640         for (var i =0 ; i < records.length;i++) {
9641             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9642             //Roo.log(records[i]);
9643             //Roo.log(this.store.getAt(rowIndex+i));
9644             this.insertRow(this.store, rowIndex + i, false);
9645             return;
9646         }
9647         
9648     },
9649     
9650     
9651     refreshRow : function(record){
9652         var ds = this.store, index;
9653         if(typeof record == 'number'){
9654             index = record;
9655             record = ds.getAt(index);
9656         }else{
9657             index = ds.indexOf(record);
9658             if (index < 0) {
9659                 return; // should not happen - but seems to 
9660             }
9661         }
9662         this.insertRow(ds, index, true);
9663         this.autoSize();
9664         this.onRemove(ds, record, index+1, true);
9665         this.autoSize();
9666         //this.syncRowHeights(index, index);
9667         //this.layout();
9668         this.fireEvent("rowupdated", this, index, record);
9669     },
9670     // private - called by RowSelection
9671     onRowSelect : function(rowIndex){
9672         var row = this.getRowDom(rowIndex);
9673         row.addClass(['bg-info','info']);
9674     },
9675     // private - called by RowSelection
9676     onRowDeselect : function(rowIndex)
9677     {
9678         if (rowIndex < 0) {
9679             return;
9680         }
9681         var row = this.getRowDom(rowIndex);
9682         row.removeClass(['bg-info','info']);
9683     },
9684       /**
9685      * Focuses the specified row.
9686      * @param {Number} row The row index
9687      */
9688     focusRow : function(row)
9689     {
9690         //Roo.log('GridView.focusRow');
9691         var x = this.bodyEl.dom.scrollLeft;
9692         this.focusCell(row, 0, false);
9693         this.bodyEl.dom.scrollLeft = x;
9694
9695     },
9696      /**
9697      * Focuses the specified cell.
9698      * @param {Number} row The row index
9699      * @param {Number} col The column index
9700      * @param {Boolean} hscroll false to disable horizontal scrolling
9701      */
9702     focusCell : function(row, col, hscroll)
9703     {
9704         //Roo.log('GridView.focusCell');
9705         var el = this.ensureVisible(row, col, hscroll);
9706         // not sure what focusEL achives = it's a <a> pos relative 
9707         //this.focusEl.alignTo(el, "tl-tl");
9708         //if(Roo.isGecko){
9709         //    this.focusEl.focus();
9710         //}else{
9711         //    this.focusEl.focus.defer(1, this.focusEl);
9712         //}
9713     },
9714     
9715      /**
9716      * Scrolls the specified cell into view
9717      * @param {Number} row The row index
9718      * @param {Number} col The column index
9719      * @param {Boolean} hscroll false to disable horizontal scrolling
9720      */
9721     ensureVisible : function(row, col, hscroll)
9722     {
9723         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9724         //return null; //disable for testing.
9725         if(typeof row != "number"){
9726             row = row.rowIndex;
9727         }
9728         if(row < 0 && row >= this.ds.getCount()){
9729             return  null;
9730         }
9731         col = (col !== undefined ? col : 0);
9732         var cm = this.cm;
9733         while(cm.isHidden(col)){
9734             col++;
9735         }
9736
9737         var el = this.getCellDom(row, col);
9738         if(!el){
9739             return null;
9740         }
9741         var c = this.bodyEl.dom;
9742
9743         var ctop = parseInt(el.offsetTop, 10);
9744         var cleft = parseInt(el.offsetLeft, 10);
9745         var cbot = ctop + el.offsetHeight;
9746         var cright = cleft + el.offsetWidth;
9747
9748         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9749         var ch = 0; //?? header is not withing the area?
9750         var stop = parseInt(c.scrollTop, 10);
9751         var sleft = parseInt(c.scrollLeft, 10);
9752         var sbot = stop + ch;
9753         var sright = sleft + c.clientWidth;
9754         /*
9755         Roo.log('GridView.ensureVisible:' +
9756                 ' ctop:' + ctop +
9757                 ' c.clientHeight:' + c.clientHeight +
9758                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9759                 ' stop:' + stop +
9760                 ' cbot:' + cbot +
9761                 ' sbot:' + sbot +
9762                 ' ch:' + ch  
9763                 );
9764         */
9765         if(ctop < stop){
9766             c.scrollTop = ctop;
9767             //Roo.log("set scrolltop to ctop DISABLE?");
9768         }else if(cbot > sbot){
9769             //Roo.log("set scrolltop to cbot-ch");
9770             c.scrollTop = cbot-ch;
9771         }
9772
9773         if(hscroll !== false){
9774             if(cleft < sleft){
9775                 c.scrollLeft = cleft;
9776             }else if(cright > sright){
9777                 c.scrollLeft = cright-c.clientWidth;
9778             }
9779         }
9780
9781         return el;
9782     },
9783     
9784     
9785     insertRow : function(dm, rowIndex, isUpdate){
9786         
9787         if(!isUpdate){
9788             this.fireEvent("beforerowsinserted", this, rowIndex);
9789         }
9790             //var s = this.getScrollState();
9791         var row = this.renderRow(this.cm, this.store, rowIndex);
9792         // insert before rowIndex..
9793         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9794         
9795         var _this = this;
9796                 
9797         if(row.cellObjects.length){
9798             Roo.each(row.cellObjects, function(r){
9799                 _this.renderCellObject(r);
9800             })
9801         }
9802             
9803         if(!isUpdate){
9804             this.fireEvent("rowsinserted", this, rowIndex);
9805             //this.syncRowHeights(firstRow, lastRow);
9806             //this.stripeRows(firstRow);
9807             //this.layout();
9808         }
9809         
9810     },
9811     
9812     
9813     getRowDom : function(rowIndex)
9814     {
9815         var rows = this.el.select('tbody > tr', true).elements;
9816         
9817         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9818         
9819     },
9820     getCellDom : function(rowIndex, colIndex)
9821     {
9822         var row = this.getRowDom(rowIndex);
9823         if (row === false) {
9824             return false;
9825         }
9826         var cols = row.select('td', true).elements;
9827         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9828         
9829     },
9830     
9831     // returns the object tree for a tr..
9832   
9833     
9834     renderRow : function(cm, ds, rowIndex) 
9835     {
9836         var d = ds.getAt(rowIndex);
9837         
9838         var row = {
9839             tag : 'tr',
9840             cls : 'x-row-' + rowIndex,
9841             cn : []
9842         };
9843             
9844         var cellObjects = [];
9845         
9846         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9847             var config = cm.config[i];
9848             
9849             var renderer = cm.getRenderer(i);
9850             var value = '';
9851             var id = false;
9852             
9853             if(typeof(renderer) !== 'undefined'){
9854                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9855             }
9856             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9857             // and are rendered into the cells after the row is rendered - using the id for the element.
9858             
9859             if(typeof(value) === 'object'){
9860                 id = Roo.id();
9861                 cellObjects.push({
9862                     container : id,
9863                     cfg : value 
9864                 })
9865             }
9866             
9867             var rowcfg = {
9868                 record: d,
9869                 rowIndex : rowIndex,
9870                 colIndex : i,
9871                 rowClass : ''
9872             };
9873
9874             this.fireEvent('rowclass', this, rowcfg);
9875             
9876             var td = {
9877                 tag: 'td',
9878                 // this might end up displaying HTML?
9879                 // this is too messy... - better to only do it on columsn you know are going to be too long
9880                 //tooltip : (typeof(value) === 'object') ? '' : value,
9881                 cls : rowcfg.rowClass + ' x-col-' + i,
9882                 style: '',
9883                 html: (typeof(value) === 'object') ? '' : value
9884             };
9885             
9886             if (id) {
9887                 td.id = id;
9888             }
9889             
9890             if(typeof(config.colspan) != 'undefined'){
9891                 td.colspan = config.colspan;
9892             }
9893             
9894             
9895             
9896             if(typeof(config.align) != 'undefined' && config.align.length){
9897                 td.style += ' text-align:' + config.align + ';';
9898             }
9899             if(typeof(config.valign) != 'undefined' && config.valign.length){
9900                 td.style += ' vertical-align:' + config.valign + ';';
9901             }
9902             /*
9903             if(typeof(config.width) != 'undefined'){
9904                 td.style += ' width:' +  config.width + 'px;';
9905             }
9906             */
9907             
9908             if(typeof(config.cursor) != 'undefined'){
9909                 td.style += ' cursor:' +  config.cursor + ';';
9910             }
9911             
9912             if(typeof(config.cls) != 'undefined'){
9913                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9914             }
9915             if (this.responsive) {
9916                 ['xs','sm','md','lg'].map(function(size){
9917                     
9918                     if(typeof(config[size]) == 'undefined'){
9919                         return;
9920                     }
9921                     
9922                     
9923                       
9924                     if (!config[size]) { // 0 = hidden
9925                         // BS 4 '0' is treated as hide that column and below.
9926                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9927                         return;
9928                     }
9929                     
9930                     td.cls += ' col-' + size + '-' + config[size] + (
9931                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9932                     );
9933                      
9934     
9935                 });
9936             }
9937             row.cn.push(td);
9938            
9939         }
9940         
9941         row.cellObjects = cellObjects;
9942         
9943         return row;
9944           
9945     },
9946     
9947     
9948     
9949     onBeforeLoad : function()
9950     {
9951         
9952     },
9953      /**
9954      * Remove all rows
9955      */
9956     clear : function()
9957     {
9958         this.el.select('tbody', true).first().dom.innerHTML = '';
9959     },
9960     /**
9961      * Show or hide a row.
9962      * @param {Number} rowIndex to show or hide
9963      * @param {Boolean} state hide
9964      */
9965     setRowVisibility : function(rowIndex, state)
9966     {
9967         var bt = this.bodyEl.dom;
9968         
9969         var rows = this.el.select('tbody > tr', true).elements;
9970         
9971         if(typeof(rows[rowIndex]) == 'undefined'){
9972             return;
9973         }
9974         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9975         
9976     },
9977     
9978     
9979     getSelectionModel : function(){
9980         if(!this.selModel){
9981             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9982         }
9983         return this.selModel;
9984     },
9985     /*
9986      * Render the Roo.bootstrap object from renderder
9987      */
9988     renderCellObject : function(r)
9989     {
9990         var _this = this;
9991         
9992         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9993         
9994         var t = r.cfg.render(r.container);
9995         
9996         if(r.cfg.cn){
9997             Roo.each(r.cfg.cn, function(c){
9998                 var child = {
9999                     container: t.getChildContainer(),
10000                     cfg: c
10001                 };
10002                 _this.renderCellObject(child);
10003             })
10004         }
10005     },
10006     /**
10007      * get the Row Index from a dom element.
10008      * @param {Roo.Element} row The row to look for
10009      * @returns {Number} the row
10010      */
10011     getRowIndex : function(row)
10012     {
10013         var rowIndex = -1;
10014         
10015         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10016             if(el != row){
10017                 return;
10018             }
10019             
10020             rowIndex = index;
10021         });
10022         
10023         return rowIndex;
10024     },
10025     /**
10026      * get the header TH element for columnIndex
10027      * @param {Number} columnIndex
10028      * @returns {Roo.Element}
10029      */
10030     getHeaderIndex: function(colIndex)
10031     {
10032         var cols = this.headEl.select('th', true).elements;
10033         return cols[colIndex]; 
10034     },
10035     /**
10036      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10037      * @param {domElement} cell to look for
10038      * @returns {Number} the column
10039      */
10040     getCellIndex : function(cell)
10041     {
10042         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10043         if(id){
10044             return parseInt(id[1], 10);
10045         }
10046         return 0;
10047     },
10048      /**
10049      * Returns the grid's underlying element = used by panel.Grid
10050      * @return {Element} The element
10051      */
10052     getGridEl : function(){
10053         return this.el;
10054     },
10055      /**
10056      * Forces a resize - used by panel.Grid
10057      * @return {Element} The element
10058      */
10059     autoSize : function()
10060     {
10061         //var ctr = Roo.get(this.container.dom.parentElement);
10062         var ctr = Roo.get(this.el.dom);
10063         
10064         var thd = this.getGridEl().select('thead',true).first();
10065         var tbd = this.getGridEl().select('tbody', true).first();
10066         var tfd = this.getGridEl().select('tfoot', true).first();
10067         
10068         var cw = ctr.getWidth();
10069         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10070         
10071         if (tbd) {
10072             
10073             tbd.setWidth(ctr.getWidth());
10074             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10075             // this needs fixing for various usage - currently only hydra job advers I think..
10076             //tdb.setHeight(
10077             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10078             //); 
10079             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10080             cw -= barsize;
10081         }
10082         cw = Math.max(cw, this.totalWidth);
10083         this.getGridEl().select('tbody tr',true).setWidth(cw);
10084         this.initCSS();
10085         
10086         // resize 'expandable coloumn?
10087         
10088         return; // we doe not have a view in this design..
10089         
10090     },
10091     onBodyScroll: function()
10092     {
10093         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10094         if(this.headEl){
10095             this.headEl.setStyle({
10096                 'position' : 'relative',
10097                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10098             });
10099         }
10100         
10101         if(this.lazyLoad){
10102             
10103             var scrollHeight = this.bodyEl.dom.scrollHeight;
10104             
10105             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10106             
10107             var height = this.bodyEl.getHeight();
10108             
10109             if(scrollHeight - height == scrollTop) {
10110                 
10111                 var total = this.ds.getTotalCount();
10112                 
10113                 if(this.footer.cursor + this.footer.pageSize < total){
10114                     
10115                     this.footer.ds.load({
10116                         params : {
10117                             start : this.footer.cursor + this.footer.pageSize,
10118                             limit : this.footer.pageSize
10119                         },
10120                         add : true
10121                     });
10122                 }
10123             }
10124             
10125         }
10126     },
10127     onColumnSplitterMoved : function(i, diff)
10128     {
10129         this.userResized = true;
10130         
10131         var cm = this.colModel;
10132         
10133         var w = this.getHeaderIndex(i).getWidth() + diff;
10134         
10135         
10136         cm.setColumnWidth(i, w, true);
10137         this.initCSS();
10138         //var cid = cm.getColumnId(i); << not used in this version?
10139        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10140         
10141         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10142         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10143         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10144 */
10145         //this.updateSplitters();
10146         //this.layout(); << ??
10147         this.fireEvent("columnresize", i, w);
10148     },
10149     onHeaderChange : function()
10150     {
10151         var header = this.renderHeader();
10152         var table = this.el.select('table', true).first();
10153         
10154         this.headEl.remove();
10155         this.headEl = table.createChild(header, this.bodyEl, false);
10156         
10157         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10158             e.on('click', this.sort, this);
10159         }, this);
10160         
10161         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10162             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10163         }
10164         
10165     },
10166     
10167     onHiddenChange : function(colModel, colIndex, hidden)
10168     {
10169         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10170         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10171         
10172         this.CSS.updateRule(thSelector, "display", "");
10173         this.CSS.updateRule(tdSelector, "display", "");
10174         
10175         if(hidden){
10176             this.CSS.updateRule(thSelector, "display", "none");
10177             this.CSS.updateRule(tdSelector, "display", "none");
10178         }
10179         
10180         this.onHeaderChange();
10181         this.onLoad();
10182     },
10183     
10184     setColumnWidth: function(col_index, width)
10185     {
10186         // width = "md-2 xs-2..."
10187         if(!this.colModel.config[col_index]) {
10188             return;
10189         }
10190         
10191         var w = width.split(" ");
10192         
10193         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10194         
10195         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10196         
10197         
10198         for(var j = 0; j < w.length; j++) {
10199             
10200             if(!w[j]) {
10201                 continue;
10202             }
10203             
10204             var size_cls = w[j].split("-");
10205             
10206             if(!Number.isInteger(size_cls[1] * 1)) {
10207                 continue;
10208             }
10209             
10210             if(!this.colModel.config[col_index][size_cls[0]]) {
10211                 continue;
10212             }
10213             
10214             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10215                 continue;
10216             }
10217             
10218             h_row[0].classList.replace(
10219                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10220                 "col-"+size_cls[0]+"-"+size_cls[1]
10221             );
10222             
10223             for(var i = 0; i < rows.length; i++) {
10224                 
10225                 var size_cls = w[j].split("-");
10226                 
10227                 if(!Number.isInteger(size_cls[1] * 1)) {
10228                     continue;
10229                 }
10230                 
10231                 if(!this.colModel.config[col_index][size_cls[0]]) {
10232                     continue;
10233                 }
10234                 
10235                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10236                     continue;
10237                 }
10238                 
10239                 rows[i].classList.replace(
10240                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10241                     "col-"+size_cls[0]+"-"+size_cls[1]
10242                 );
10243             }
10244             
10245             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10246         }
10247     }
10248 });
10249
10250 // currently only used to find the split on drag.. 
10251 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10252
10253 /**
10254  * @depricated
10255 */
10256 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10257 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10258 /*
10259  * - LGPL
10260  *
10261  * table cell
10262  * 
10263  */
10264
10265 /**
10266  * @class Roo.bootstrap.TableCell
10267  * @extends Roo.bootstrap.Component
10268  * Bootstrap TableCell class
10269  * @cfg {String} html cell contain text
10270  * @cfg {String} cls cell class
10271  * @cfg {String} tag cell tag (td|th) default td
10272  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10273  * @cfg {String} align Aligns the content in a cell
10274  * @cfg {String} axis Categorizes cells
10275  * @cfg {String} bgcolor Specifies the background color of a cell
10276  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10277  * @cfg {Number} colspan Specifies the number of columns a cell should span
10278  * @cfg {String} headers Specifies one or more header cells a cell is related to
10279  * @cfg {Number} height Sets the height of a cell
10280  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10281  * @cfg {Number} rowspan Sets the number of rows a cell should span
10282  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10283  * @cfg {String} valign Vertical aligns the content in a cell
10284  * @cfg {Number} width Specifies the width of a cell
10285  * 
10286  * @constructor
10287  * Create a new TableCell
10288  * @param {Object} config The config object
10289  */
10290
10291 Roo.bootstrap.TableCell = function(config){
10292     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10293 };
10294
10295 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10296     
10297     html: false,
10298     cls: false,
10299     tag: false,
10300     abbr: false,
10301     align: false,
10302     axis: false,
10303     bgcolor: false,
10304     charoff: false,
10305     colspan: false,
10306     headers: false,
10307     height: false,
10308     nowrap: false,
10309     rowspan: false,
10310     scope: false,
10311     valign: false,
10312     width: false,
10313     
10314     
10315     getAutoCreate : function(){
10316         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10317         
10318         cfg = {
10319             tag: 'td'
10320         };
10321         
10322         if(this.tag){
10323             cfg.tag = this.tag;
10324         }
10325         
10326         if (this.html) {
10327             cfg.html=this.html
10328         }
10329         if (this.cls) {
10330             cfg.cls=this.cls
10331         }
10332         if (this.abbr) {
10333             cfg.abbr=this.abbr
10334         }
10335         if (this.align) {
10336             cfg.align=this.align
10337         }
10338         if (this.axis) {
10339             cfg.axis=this.axis
10340         }
10341         if (this.bgcolor) {
10342             cfg.bgcolor=this.bgcolor
10343         }
10344         if (this.charoff) {
10345             cfg.charoff=this.charoff
10346         }
10347         if (this.colspan) {
10348             cfg.colspan=this.colspan
10349         }
10350         if (this.headers) {
10351             cfg.headers=this.headers
10352         }
10353         if (this.height) {
10354             cfg.height=this.height
10355         }
10356         if (this.nowrap) {
10357             cfg.nowrap=this.nowrap
10358         }
10359         if (this.rowspan) {
10360             cfg.rowspan=this.rowspan
10361         }
10362         if (this.scope) {
10363             cfg.scope=this.scope
10364         }
10365         if (this.valign) {
10366             cfg.valign=this.valign
10367         }
10368         if (this.width) {
10369             cfg.width=this.width
10370         }
10371         
10372         
10373         return cfg;
10374     }
10375    
10376 });
10377
10378  
10379
10380  /*
10381  * - LGPL
10382  *
10383  * table row
10384  * 
10385  */
10386
10387 /**
10388  * @class Roo.bootstrap.TableRow
10389  * @extends Roo.bootstrap.Component
10390  * Bootstrap TableRow class
10391  * @cfg {String} cls row class
10392  * @cfg {String} align Aligns the content in a table row
10393  * @cfg {String} bgcolor Specifies a background color for a table row
10394  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10395  * @cfg {String} valign Vertical aligns the content in a table row
10396  * 
10397  * @constructor
10398  * Create a new TableRow
10399  * @param {Object} config The config object
10400  */
10401
10402 Roo.bootstrap.TableRow = function(config){
10403     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10404 };
10405
10406 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10407     
10408     cls: false,
10409     align: false,
10410     bgcolor: false,
10411     charoff: false,
10412     valign: false,
10413     
10414     getAutoCreate : function(){
10415         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10416         
10417         cfg = {
10418             tag: 'tr'
10419         };
10420             
10421         if(this.cls){
10422             cfg.cls = this.cls;
10423         }
10424         if(this.align){
10425             cfg.align = this.align;
10426         }
10427         if(this.bgcolor){
10428             cfg.bgcolor = this.bgcolor;
10429         }
10430         if(this.charoff){
10431             cfg.charoff = this.charoff;
10432         }
10433         if(this.valign){
10434             cfg.valign = this.valign;
10435         }
10436         
10437         return cfg;
10438     }
10439    
10440 });
10441
10442  
10443
10444  /*
10445  * - LGPL
10446  *
10447  * table body
10448  * 
10449  */
10450
10451 /**
10452  * @class Roo.bootstrap.TableBody
10453  * @extends Roo.bootstrap.Component
10454  * Bootstrap TableBody class
10455  * @cfg {String} cls element class
10456  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10457  * @cfg {String} align Aligns the content inside the element
10458  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10459  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10460  * 
10461  * @constructor
10462  * Create a new TableBody
10463  * @param {Object} config The config object
10464  */
10465
10466 Roo.bootstrap.TableBody = function(config){
10467     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10468 };
10469
10470 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10471     
10472     cls: false,
10473     tag: false,
10474     align: false,
10475     charoff: false,
10476     valign: false,
10477     
10478     getAutoCreate : function(){
10479         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10480         
10481         cfg = {
10482             tag: 'tbody'
10483         };
10484             
10485         if (this.cls) {
10486             cfg.cls=this.cls
10487         }
10488         if(this.tag){
10489             cfg.tag = this.tag;
10490         }
10491         
10492         if(this.align){
10493             cfg.align = this.align;
10494         }
10495         if(this.charoff){
10496             cfg.charoff = this.charoff;
10497         }
10498         if(this.valign){
10499             cfg.valign = this.valign;
10500         }
10501         
10502         return cfg;
10503     }
10504     
10505     
10506 //    initEvents : function()
10507 //    {
10508 //        
10509 //        if(!this.store){
10510 //            return;
10511 //        }
10512 //        
10513 //        this.store = Roo.factory(this.store, Roo.data);
10514 //        this.store.on('load', this.onLoad, this);
10515 //        
10516 //        this.store.load();
10517 //        
10518 //    },
10519 //    
10520 //    onLoad: function () 
10521 //    {   
10522 //        this.fireEvent('load', this);
10523 //    }
10524 //    
10525 //   
10526 });
10527
10528  
10529
10530  /*
10531  * Based on:
10532  * Ext JS Library 1.1.1
10533  * Copyright(c) 2006-2007, Ext JS, LLC.
10534  *
10535  * Originally Released Under LGPL - original licence link has changed is not relivant.
10536  *
10537  * Fork - LGPL
10538  * <script type="text/javascript">
10539  */
10540
10541 // as we use this in bootstrap.
10542 Roo.namespace('Roo.form');
10543  /**
10544  * @class Roo.form.Action
10545  * Internal Class used to handle form actions
10546  * @constructor
10547  * @param {Roo.form.BasicForm} el The form element or its id
10548  * @param {Object} config Configuration options
10549  */
10550
10551  
10552  
10553 // define the action interface
10554 Roo.form.Action = function(form, options){
10555     this.form = form;
10556     this.options = options || {};
10557 };
10558 /**
10559  * Client Validation Failed
10560  * @const 
10561  */
10562 Roo.form.Action.CLIENT_INVALID = 'client';
10563 /**
10564  * Server Validation Failed
10565  * @const 
10566  */
10567 Roo.form.Action.SERVER_INVALID = 'server';
10568  /**
10569  * Connect to Server Failed
10570  * @const 
10571  */
10572 Roo.form.Action.CONNECT_FAILURE = 'connect';
10573 /**
10574  * Reading Data from Server Failed
10575  * @const 
10576  */
10577 Roo.form.Action.LOAD_FAILURE = 'load';
10578
10579 Roo.form.Action.prototype = {
10580     type : 'default',
10581     failureType : undefined,
10582     response : undefined,
10583     result : undefined,
10584
10585     // interface method
10586     run : function(options){
10587
10588     },
10589
10590     // interface method
10591     success : function(response){
10592
10593     },
10594
10595     // interface method
10596     handleResponse : function(response){
10597
10598     },
10599
10600     // default connection failure
10601     failure : function(response){
10602         
10603         this.response = response;
10604         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10605         this.form.afterAction(this, false);
10606     },
10607
10608     processResponse : function(response){
10609         this.response = response;
10610         if(!response.responseText){
10611             return true;
10612         }
10613         this.result = this.handleResponse(response);
10614         return this.result;
10615     },
10616
10617     // utility functions used internally
10618     getUrl : function(appendParams){
10619         var url = this.options.url || this.form.url || this.form.el.dom.action;
10620         if(appendParams){
10621             var p = this.getParams();
10622             if(p){
10623                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10624             }
10625         }
10626         return url;
10627     },
10628
10629     getMethod : function(){
10630         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10631     },
10632
10633     getParams : function(){
10634         var bp = this.form.baseParams;
10635         var p = this.options.params;
10636         if(p){
10637             if(typeof p == "object"){
10638                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10639             }else if(typeof p == 'string' && bp){
10640                 p += '&' + Roo.urlEncode(bp);
10641             }
10642         }else if(bp){
10643             p = Roo.urlEncode(bp);
10644         }
10645         return p;
10646     },
10647
10648     createCallback : function(){
10649         return {
10650             success: this.success,
10651             failure: this.failure,
10652             scope: this,
10653             timeout: (this.form.timeout*1000),
10654             upload: this.form.fileUpload ? this.success : undefined
10655         };
10656     }
10657 };
10658
10659 Roo.form.Action.Submit = function(form, options){
10660     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10661 };
10662
10663 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10664     type : 'submit',
10665
10666     haveProgress : false,
10667     uploadComplete : false,
10668     
10669     // uploadProgress indicator.
10670     uploadProgress : function()
10671     {
10672         if (!this.form.progressUrl) {
10673             return;
10674         }
10675         
10676         if (!this.haveProgress) {
10677             Roo.MessageBox.progress("Uploading", "Uploading");
10678         }
10679         if (this.uploadComplete) {
10680            Roo.MessageBox.hide();
10681            return;
10682         }
10683         
10684         this.haveProgress = true;
10685    
10686         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10687         
10688         var c = new Roo.data.Connection();
10689         c.request({
10690             url : this.form.progressUrl,
10691             params: {
10692                 id : uid
10693             },
10694             method: 'GET',
10695             success : function(req){
10696                //console.log(data);
10697                 var rdata = false;
10698                 var edata;
10699                 try  {
10700                    rdata = Roo.decode(req.responseText)
10701                 } catch (e) {
10702                     Roo.log("Invalid data from server..");
10703                     Roo.log(edata);
10704                     return;
10705                 }
10706                 if (!rdata || !rdata.success) {
10707                     Roo.log(rdata);
10708                     Roo.MessageBox.alert(Roo.encode(rdata));
10709                     return;
10710                 }
10711                 var data = rdata.data;
10712                 
10713                 if (this.uploadComplete) {
10714                    Roo.MessageBox.hide();
10715                    return;
10716                 }
10717                    
10718                 if (data){
10719                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10720                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10721                     );
10722                 }
10723                 this.uploadProgress.defer(2000,this);
10724             },
10725        
10726             failure: function(data) {
10727                 Roo.log('progress url failed ');
10728                 Roo.log(data);
10729             },
10730             scope : this
10731         });
10732            
10733     },
10734     
10735     
10736     run : function()
10737     {
10738         // run get Values on the form, so it syncs any secondary forms.
10739         this.form.getValues();
10740         
10741         var o = this.options;
10742         var method = this.getMethod();
10743         var isPost = method == 'POST';
10744         if(o.clientValidation === false || this.form.isValid()){
10745             
10746             if (this.form.progressUrl) {
10747                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10748                     (new Date() * 1) + '' + Math.random());
10749                     
10750             } 
10751             
10752             
10753             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10754                 form:this.form.el.dom,
10755                 url:this.getUrl(!isPost),
10756                 method: method,
10757                 params:isPost ? this.getParams() : null,
10758                 isUpload: this.form.fileUpload,
10759                 formData : this.form.formData
10760             }));
10761             
10762             this.uploadProgress();
10763
10764         }else if (o.clientValidation !== false){ // client validation failed
10765             this.failureType = Roo.form.Action.CLIENT_INVALID;
10766             this.form.afterAction(this, false);
10767         }
10768     },
10769
10770     success : function(response)
10771     {
10772         this.uploadComplete= true;
10773         if (this.haveProgress) {
10774             Roo.MessageBox.hide();
10775         }
10776         
10777         
10778         var result = this.processResponse(response);
10779         if(result === true || result.success){
10780             this.form.afterAction(this, true);
10781             return;
10782         }
10783         if(result.errors){
10784             this.form.markInvalid(result.errors);
10785             this.failureType = Roo.form.Action.SERVER_INVALID;
10786         }
10787         this.form.afterAction(this, false);
10788     },
10789     failure : function(response)
10790     {
10791         this.uploadComplete= true;
10792         if (this.haveProgress) {
10793             Roo.MessageBox.hide();
10794         }
10795         
10796         this.response = response;
10797         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10798         this.form.afterAction(this, false);
10799     },
10800     
10801     handleResponse : function(response){
10802         if(this.form.errorReader){
10803             var rs = this.form.errorReader.read(response);
10804             var errors = [];
10805             if(rs.records){
10806                 for(var i = 0, len = rs.records.length; i < len; i++) {
10807                     var r = rs.records[i];
10808                     errors[i] = r.data;
10809                 }
10810             }
10811             if(errors.length < 1){
10812                 errors = null;
10813             }
10814             return {
10815                 success : rs.success,
10816                 errors : errors
10817             };
10818         }
10819         var ret = false;
10820         try {
10821             ret = Roo.decode(response.responseText);
10822         } catch (e) {
10823             ret = {
10824                 success: false,
10825                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10826                 errors : []
10827             };
10828         }
10829         return ret;
10830         
10831     }
10832 });
10833
10834
10835 Roo.form.Action.Load = function(form, options){
10836     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10837     this.reader = this.form.reader;
10838 };
10839
10840 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10841     type : 'load',
10842
10843     run : function(){
10844         
10845         Roo.Ajax.request(Roo.apply(
10846                 this.createCallback(), {
10847                     method:this.getMethod(),
10848                     url:this.getUrl(false),
10849                     params:this.getParams()
10850         }));
10851     },
10852
10853     success : function(response){
10854         
10855         var result = this.processResponse(response);
10856         if(result === true || !result.success || !result.data){
10857             this.failureType = Roo.form.Action.LOAD_FAILURE;
10858             this.form.afterAction(this, false);
10859             return;
10860         }
10861         this.form.clearInvalid();
10862         this.form.setValues(result.data);
10863         this.form.afterAction(this, true);
10864     },
10865
10866     handleResponse : function(response){
10867         if(this.form.reader){
10868             var rs = this.form.reader.read(response);
10869             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10870             return {
10871                 success : rs.success,
10872                 data : data
10873             };
10874         }
10875         return Roo.decode(response.responseText);
10876     }
10877 });
10878
10879 Roo.form.Action.ACTION_TYPES = {
10880     'load' : Roo.form.Action.Load,
10881     'submit' : Roo.form.Action.Submit
10882 };/*
10883  * - LGPL
10884  *
10885  * form
10886  *
10887  */
10888
10889 /**
10890  * @class Roo.bootstrap.Form
10891  * @extends Roo.bootstrap.Component
10892  * Bootstrap Form class
10893  * @cfg {String} method  GET | POST (default POST)
10894  * @cfg {String} labelAlign top | left (default top)
10895  * @cfg {String} align left  | right - for navbars
10896  * @cfg {Boolean} loadMask load mask when submit (default true)
10897
10898  *
10899  * @constructor
10900  * Create a new Form
10901  * @param {Object} config The config object
10902  */
10903
10904
10905 Roo.bootstrap.Form = function(config){
10906     
10907     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10908     
10909     Roo.bootstrap.Form.popover.apply();
10910     
10911     this.addEvents({
10912         /**
10913          * @event clientvalidation
10914          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10915          * @param {Form} this
10916          * @param {Boolean} valid true if the form has passed client-side validation
10917          */
10918         clientvalidation: true,
10919         /**
10920          * @event beforeaction
10921          * Fires before any action is performed. Return false to cancel the action.
10922          * @param {Form} this
10923          * @param {Action} action The action to be performed
10924          */
10925         beforeaction: true,
10926         /**
10927          * @event actionfailed
10928          * Fires when an action fails.
10929          * @param {Form} this
10930          * @param {Action} action The action that failed
10931          */
10932         actionfailed : true,
10933         /**
10934          * @event actioncomplete
10935          * Fires when an action is completed.
10936          * @param {Form} this
10937          * @param {Action} action The action that completed
10938          */
10939         actioncomplete : true
10940     });
10941 };
10942
10943 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10944
10945      /**
10946      * @cfg {String} method
10947      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10948      */
10949     method : 'POST',
10950     /**
10951      * @cfg {String} url
10952      * The URL to use for form actions if one isn't supplied in the action options.
10953      */
10954     /**
10955      * @cfg {Boolean} fileUpload
10956      * Set to true if this form is a file upload.
10957      */
10958
10959     /**
10960      * @cfg {Object} baseParams
10961      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10962      */
10963
10964     /**
10965      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10966      */
10967     timeout: 30,
10968     /**
10969      * @cfg {Sting} align (left|right) for navbar forms
10970      */
10971     align : 'left',
10972
10973     // private
10974     activeAction : null,
10975
10976     /**
10977      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10978      * element by passing it or its id or mask the form itself by passing in true.
10979      * @type Mixed
10980      */
10981     waitMsgTarget : false,
10982
10983     loadMask : true,
10984     
10985     /**
10986      * @cfg {Boolean} errorMask (true|false) default false
10987      */
10988     errorMask : false,
10989     
10990     /**
10991      * @cfg {Number} maskOffset Default 100
10992      */
10993     maskOffset : 100,
10994     
10995     /**
10996      * @cfg {Boolean} maskBody
10997      */
10998     maskBody : false,
10999
11000     getAutoCreate : function(){
11001
11002         var cfg = {
11003             tag: 'form',
11004             method : this.method || 'POST',
11005             id : this.id || Roo.id(),
11006             cls : ''
11007         };
11008         if (this.parent().xtype.match(/^Nav/)) {
11009             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11010
11011         }
11012
11013         if (this.labelAlign == 'left' ) {
11014             cfg.cls += ' form-horizontal';
11015         }
11016
11017
11018         return cfg;
11019     },
11020     initEvents : function()
11021     {
11022         this.el.on('submit', this.onSubmit, this);
11023         // this was added as random key presses on the form where triggering form submit.
11024         this.el.on('keypress', function(e) {
11025             if (e.getCharCode() != 13) {
11026                 return true;
11027             }
11028             // we might need to allow it for textareas.. and some other items.
11029             // check e.getTarget().
11030
11031             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11032                 return true;
11033             }
11034
11035             Roo.log("keypress blocked");
11036
11037             e.preventDefault();
11038             return false;
11039         });
11040         
11041     },
11042     // private
11043     onSubmit : function(e){
11044         e.stopEvent();
11045     },
11046
11047      /**
11048      * Returns true if client-side validation on the form is successful.
11049      * @return Boolean
11050      */
11051     isValid : function(){
11052         var items = this.getItems();
11053         var valid = true;
11054         var target = false;
11055         
11056         items.each(function(f){
11057             
11058             if(f.validate()){
11059                 return;
11060             }
11061             
11062             Roo.log('invalid field: ' + f.name);
11063             
11064             valid = false;
11065
11066             if(!target && f.el.isVisible(true)){
11067                 target = f;
11068             }
11069            
11070         });
11071         
11072         if(this.errorMask && !valid){
11073             Roo.bootstrap.Form.popover.mask(this, target);
11074         }
11075         
11076         return valid;
11077     },
11078     
11079     /**
11080      * Returns true if any fields in this form have changed since their original load.
11081      * @return Boolean
11082      */
11083     isDirty : function(){
11084         var dirty = false;
11085         var items = this.getItems();
11086         items.each(function(f){
11087            if(f.isDirty()){
11088                dirty = true;
11089                return false;
11090            }
11091            return true;
11092         });
11093         return dirty;
11094     },
11095      /**
11096      * Performs a predefined action (submit or load) or custom actions you define on this form.
11097      * @param {String} actionName The name of the action type
11098      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11099      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11100      * accept other config options):
11101      * <pre>
11102 Property          Type             Description
11103 ----------------  ---------------  ----------------------------------------------------------------------------------
11104 url               String           The url for the action (defaults to the form's url)
11105 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11106 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11107 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11108                                    validate the form on the client (defaults to false)
11109      * </pre>
11110      * @return {BasicForm} this
11111      */
11112     doAction : function(action, options){
11113         if(typeof action == 'string'){
11114             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11115         }
11116         if(this.fireEvent('beforeaction', this, action) !== false){
11117             this.beforeAction(action);
11118             action.run.defer(100, action);
11119         }
11120         return this;
11121     },
11122
11123     // private
11124     beforeAction : function(action){
11125         var o = action.options;
11126         
11127         if(this.loadMask){
11128             
11129             if(this.maskBody){
11130                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11131             } else {
11132                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11133             }
11134         }
11135         // not really supported yet.. ??
11136
11137         //if(this.waitMsgTarget === true){
11138         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11139         //}else if(this.waitMsgTarget){
11140         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11141         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11142         //}else {
11143         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11144        // }
11145
11146     },
11147
11148     // private
11149     afterAction : function(action, success){
11150         this.activeAction = null;
11151         var o = action.options;
11152
11153         if(this.loadMask){
11154             
11155             if(this.maskBody){
11156                 Roo.get(document.body).unmask();
11157             } else {
11158                 this.el.unmask();
11159             }
11160         }
11161         
11162         //if(this.waitMsgTarget === true){
11163 //            this.el.unmask();
11164         //}else if(this.waitMsgTarget){
11165         //    this.waitMsgTarget.unmask();
11166         //}else{
11167         //    Roo.MessageBox.updateProgress(1);
11168         //    Roo.MessageBox.hide();
11169        // }
11170         //
11171         if(success){
11172             if(o.reset){
11173                 this.reset();
11174             }
11175             Roo.callback(o.success, o.scope, [this, action]);
11176             this.fireEvent('actioncomplete', this, action);
11177
11178         }else{
11179
11180             // failure condition..
11181             // we have a scenario where updates need confirming.
11182             // eg. if a locking scenario exists..
11183             // we look for { errors : { needs_confirm : true }} in the response.
11184             if (
11185                 (typeof(action.result) != 'undefined')  &&
11186                 (typeof(action.result.errors) != 'undefined')  &&
11187                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11188            ){
11189                 var _t = this;
11190                 Roo.log("not supported yet");
11191                  /*
11192
11193                 Roo.MessageBox.confirm(
11194                     "Change requires confirmation",
11195                     action.result.errorMsg,
11196                     function(r) {
11197                         if (r != 'yes') {
11198                             return;
11199                         }
11200                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11201                     }
11202
11203                 );
11204                 */
11205
11206
11207                 return;
11208             }
11209
11210             Roo.callback(o.failure, o.scope, [this, action]);
11211             // show an error message if no failed handler is set..
11212             if (!this.hasListener('actionfailed')) {
11213                 Roo.log("need to add dialog support");
11214                 /*
11215                 Roo.MessageBox.alert("Error",
11216                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11217                         action.result.errorMsg :
11218                         "Saving Failed, please check your entries or try again"
11219                 );
11220                 */
11221             }
11222
11223             this.fireEvent('actionfailed', this, action);
11224         }
11225
11226     },
11227     /**
11228      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11229      * @param {String} id The value to search for
11230      * @return Field
11231      */
11232     findField : function(id){
11233         var items = this.getItems();
11234         var field = items.get(id);
11235         if(!field){
11236              items.each(function(f){
11237                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11238                     field = f;
11239                     return false;
11240                 }
11241                 return true;
11242             });
11243         }
11244         return field || null;
11245     },
11246      /**
11247      * Mark fields in this form invalid in bulk.
11248      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11249      * @return {BasicForm} this
11250      */
11251     markInvalid : function(errors){
11252         if(errors instanceof Array){
11253             for(var i = 0, len = errors.length; i < len; i++){
11254                 var fieldError = errors[i];
11255                 var f = this.findField(fieldError.id);
11256                 if(f){
11257                     f.markInvalid(fieldError.msg);
11258                 }
11259             }
11260         }else{
11261             var field, id;
11262             for(id in errors){
11263                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11264                     field.markInvalid(errors[id]);
11265                 }
11266             }
11267         }
11268         //Roo.each(this.childForms || [], function (f) {
11269         //    f.markInvalid(errors);
11270         //});
11271
11272         return this;
11273     },
11274
11275     /**
11276      * Set values for fields in this form in bulk.
11277      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11278      * @return {BasicForm} this
11279      */
11280     setValues : function(values){
11281         if(values instanceof Array){ // array of objects
11282             for(var i = 0, len = values.length; i < len; i++){
11283                 var v = values[i];
11284                 var f = this.findField(v.id);
11285                 if(f){
11286                     f.setValue(v.value);
11287                     if(this.trackResetOnLoad){
11288                         f.originalValue = f.getValue();
11289                     }
11290                 }
11291             }
11292         }else{ // object hash
11293             var field, id;
11294             for(id in values){
11295                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11296
11297                     if (field.setFromData &&
11298                         field.valueField &&
11299                         field.displayField &&
11300                         // combos' with local stores can
11301                         // be queried via setValue()
11302                         // to set their value..
11303                         (field.store && !field.store.isLocal)
11304                         ) {
11305                         // it's a combo
11306                         var sd = { };
11307                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11308                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11309                         field.setFromData(sd);
11310
11311                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11312                         
11313                         field.setFromData(values);
11314                         
11315                     } else {
11316                         field.setValue(values[id]);
11317                     }
11318
11319
11320                     if(this.trackResetOnLoad){
11321                         field.originalValue = field.getValue();
11322                     }
11323                 }
11324             }
11325         }
11326
11327         //Roo.each(this.childForms || [], function (f) {
11328         //    f.setValues(values);
11329         //});
11330
11331         return this;
11332     },
11333
11334     /**
11335      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11336      * they are returned as an array.
11337      * @param {Boolean} asString
11338      * @return {Object}
11339      */
11340     getValues : function(asString){
11341         //if (this.childForms) {
11342             // copy values from the child forms
11343         //    Roo.each(this.childForms, function (f) {
11344         //        this.setValues(f.getValues());
11345         //    }, this);
11346         //}
11347
11348
11349
11350         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11351         if(asString === true){
11352             return fs;
11353         }
11354         return Roo.urlDecode(fs);
11355     },
11356
11357     /**
11358      * Returns the fields in this form as an object with key/value pairs.
11359      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11360      * @return {Object}
11361      */
11362     getFieldValues : function(with_hidden)
11363     {
11364         var items = this.getItems();
11365         var ret = {};
11366         items.each(function(f){
11367             
11368             if (!f.getName()) {
11369                 return;
11370             }
11371             
11372             var v = f.getValue();
11373             
11374             if (f.inputType =='radio') {
11375                 if (typeof(ret[f.getName()]) == 'undefined') {
11376                     ret[f.getName()] = ''; // empty..
11377                 }
11378
11379                 if (!f.el.dom.checked) {
11380                     return;
11381
11382                 }
11383                 v = f.el.dom.value;
11384
11385             }
11386             
11387             if(f.xtype == 'MoneyField'){
11388                 ret[f.currencyName] = f.getCurrency();
11389             }
11390
11391             // not sure if this supported any more..
11392             if ((typeof(v) == 'object') && f.getRawValue) {
11393                 v = f.getRawValue() ; // dates..
11394             }
11395             // combo boxes where name != hiddenName...
11396             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11397                 ret[f.name] = f.getRawValue();
11398             }
11399             ret[f.getName()] = v;
11400         });
11401
11402         return ret;
11403     },
11404
11405     /**
11406      * Clears all invalid messages in this form.
11407      * @return {BasicForm} this
11408      */
11409     clearInvalid : function(){
11410         var items = this.getItems();
11411
11412         items.each(function(f){
11413            f.clearInvalid();
11414         });
11415
11416         return this;
11417     },
11418
11419     /**
11420      * Resets this form.
11421      * @return {BasicForm} this
11422      */
11423     reset : function(){
11424         var items = this.getItems();
11425         items.each(function(f){
11426             f.reset();
11427         });
11428
11429         Roo.each(this.childForms || [], function (f) {
11430             f.reset();
11431         });
11432
11433
11434         return this;
11435     },
11436     
11437     getItems : function()
11438     {
11439         var r=new Roo.util.MixedCollection(false, function(o){
11440             return o.id || (o.id = Roo.id());
11441         });
11442         var iter = function(el) {
11443             if (el.inputEl) {
11444                 r.add(el);
11445             }
11446             if (!el.items) {
11447                 return;
11448             }
11449             Roo.each(el.items,function(e) {
11450                 iter(e);
11451             });
11452         };
11453
11454         iter(this);
11455         return r;
11456     },
11457     
11458     hideFields : function(items)
11459     {
11460         Roo.each(items, function(i){
11461             
11462             var f = this.findField(i);
11463             
11464             if(!f){
11465                 return;
11466             }
11467             
11468             f.hide();
11469             
11470         }, this);
11471     },
11472     
11473     showFields : function(items)
11474     {
11475         Roo.each(items, function(i){
11476             
11477             var f = this.findField(i);
11478             
11479             if(!f){
11480                 return;
11481             }
11482             
11483             f.show();
11484             
11485         }, this);
11486     }
11487
11488 });
11489
11490 Roo.apply(Roo.bootstrap.Form, {
11491     
11492     popover : {
11493         
11494         padding : 5,
11495         
11496         isApplied : false,
11497         
11498         isMasked : false,
11499         
11500         form : false,
11501         
11502         target : false,
11503         
11504         toolTip : false,
11505         
11506         intervalID : false,
11507         
11508         maskEl : false,
11509         
11510         apply : function()
11511         {
11512             if(this.isApplied){
11513                 return;
11514             }
11515             
11516             this.maskEl = {
11517                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11518                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11519                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11520                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11521             };
11522             
11523             this.maskEl.top.enableDisplayMode("block");
11524             this.maskEl.left.enableDisplayMode("block");
11525             this.maskEl.bottom.enableDisplayMode("block");
11526             this.maskEl.right.enableDisplayMode("block");
11527             
11528             this.toolTip = new Roo.bootstrap.Tooltip({
11529                 cls : 'roo-form-error-popover',
11530                 alignment : {
11531                     'left' : ['r-l', [-2,0], 'right'],
11532                     'right' : ['l-r', [2,0], 'left'],
11533                     'bottom' : ['tl-bl', [0,2], 'top'],
11534                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11535                 }
11536             });
11537             
11538             this.toolTip.render(Roo.get(document.body));
11539
11540             this.toolTip.el.enableDisplayMode("block");
11541             
11542             Roo.get(document.body).on('click', function(){
11543                 this.unmask();
11544             }, this);
11545             
11546             Roo.get(document.body).on('touchstart', function(){
11547                 this.unmask();
11548             }, this);
11549             
11550             this.isApplied = true
11551         },
11552         
11553         mask : function(form, target)
11554         {
11555             this.form = form;
11556             
11557             this.target = target;
11558             
11559             if(!this.form.errorMask || !target.el){
11560                 return;
11561             }
11562             
11563             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11564             
11565             Roo.log(scrollable);
11566             
11567             var ot = this.target.el.calcOffsetsTo(scrollable);
11568             
11569             var scrollTo = ot[1] - this.form.maskOffset;
11570             
11571             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11572             
11573             scrollable.scrollTo('top', scrollTo);
11574             
11575             var box = this.target.el.getBox();
11576             Roo.log(box);
11577             var zIndex = Roo.bootstrap.Modal.zIndex++;
11578
11579             
11580             this.maskEl.top.setStyle('position', 'absolute');
11581             this.maskEl.top.setStyle('z-index', zIndex);
11582             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11583             this.maskEl.top.setLeft(0);
11584             this.maskEl.top.setTop(0);
11585             this.maskEl.top.show();
11586             
11587             this.maskEl.left.setStyle('position', 'absolute');
11588             this.maskEl.left.setStyle('z-index', zIndex);
11589             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11590             this.maskEl.left.setLeft(0);
11591             this.maskEl.left.setTop(box.y - this.padding);
11592             this.maskEl.left.show();
11593
11594             this.maskEl.bottom.setStyle('position', 'absolute');
11595             this.maskEl.bottom.setStyle('z-index', zIndex);
11596             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11597             this.maskEl.bottom.setLeft(0);
11598             this.maskEl.bottom.setTop(box.bottom + this.padding);
11599             this.maskEl.bottom.show();
11600
11601             this.maskEl.right.setStyle('position', 'absolute');
11602             this.maskEl.right.setStyle('z-index', zIndex);
11603             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11604             this.maskEl.right.setLeft(box.right + this.padding);
11605             this.maskEl.right.setTop(box.y - this.padding);
11606             this.maskEl.right.show();
11607
11608             this.toolTip.bindEl = this.target.el;
11609
11610             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11611
11612             var tip = this.target.blankText;
11613
11614             if(this.target.getValue() !== '' ) {
11615                 
11616                 if (this.target.invalidText.length) {
11617                     tip = this.target.invalidText;
11618                 } else if (this.target.regexText.length){
11619                     tip = this.target.regexText;
11620                 }
11621             }
11622
11623             this.toolTip.show(tip);
11624
11625             this.intervalID = window.setInterval(function() {
11626                 Roo.bootstrap.Form.popover.unmask();
11627             }, 10000);
11628
11629             window.onwheel = function(){ return false;};
11630             
11631             (function(){ this.isMasked = true; }).defer(500, this);
11632             
11633         },
11634         
11635         unmask : function()
11636         {
11637             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11638                 return;
11639             }
11640             
11641             this.maskEl.top.setStyle('position', 'absolute');
11642             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11643             this.maskEl.top.hide();
11644
11645             this.maskEl.left.setStyle('position', 'absolute');
11646             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11647             this.maskEl.left.hide();
11648
11649             this.maskEl.bottom.setStyle('position', 'absolute');
11650             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11651             this.maskEl.bottom.hide();
11652
11653             this.maskEl.right.setStyle('position', 'absolute');
11654             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11655             this.maskEl.right.hide();
11656             
11657             this.toolTip.hide();
11658             
11659             this.toolTip.el.hide();
11660             
11661             window.onwheel = function(){ return true;};
11662             
11663             if(this.intervalID){
11664                 window.clearInterval(this.intervalID);
11665                 this.intervalID = false;
11666             }
11667             
11668             this.isMasked = false;
11669             
11670         }
11671         
11672     }
11673     
11674 });
11675
11676 /*
11677  * Based on:
11678  * Ext JS Library 1.1.1
11679  * Copyright(c) 2006-2007, Ext JS, LLC.
11680  *
11681  * Originally Released Under LGPL - original licence link has changed is not relivant.
11682  *
11683  * Fork - LGPL
11684  * <script type="text/javascript">
11685  */
11686 /**
11687  * @class Roo.form.VTypes
11688  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11689  * @singleton
11690  */
11691 Roo.form.VTypes = function(){
11692     // closure these in so they are only created once.
11693     var alpha = /^[a-zA-Z_]+$/;
11694     var alphanum = /^[a-zA-Z0-9_]+$/;
11695     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11696     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11697
11698     // All these messages and functions are configurable
11699     return {
11700         /**
11701          * The function used to validate email addresses
11702          * @param {String} value The email address
11703          */
11704         'email' : function(v){
11705             return email.test(v);
11706         },
11707         /**
11708          * The error text to display when the email validation function returns false
11709          * @type String
11710          */
11711         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11712         /**
11713          * The keystroke filter mask to be applied on email input
11714          * @type RegExp
11715          */
11716         'emailMask' : /[a-z0-9_\.\-@]/i,
11717
11718         /**
11719          * The function used to validate URLs
11720          * @param {String} value The URL
11721          */
11722         'url' : function(v){
11723             return url.test(v);
11724         },
11725         /**
11726          * The error text to display when the url validation function returns false
11727          * @type String
11728          */
11729         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11730         
11731         /**
11732          * The function used to validate alpha values
11733          * @param {String} value The value
11734          */
11735         'alpha' : function(v){
11736             return alpha.test(v);
11737         },
11738         /**
11739          * The error text to display when the alpha validation function returns false
11740          * @type String
11741          */
11742         'alphaText' : 'This field should only contain letters and _',
11743         /**
11744          * The keystroke filter mask to be applied on alpha input
11745          * @type RegExp
11746          */
11747         'alphaMask' : /[a-z_]/i,
11748
11749         /**
11750          * The function used to validate alphanumeric values
11751          * @param {String} value The value
11752          */
11753         'alphanum' : function(v){
11754             return alphanum.test(v);
11755         },
11756         /**
11757          * The error text to display when the alphanumeric validation function returns false
11758          * @type String
11759          */
11760         'alphanumText' : 'This field should only contain letters, numbers and _',
11761         /**
11762          * The keystroke filter mask to be applied on alphanumeric input
11763          * @type RegExp
11764          */
11765         'alphanumMask' : /[a-z0-9_]/i
11766     };
11767 }();/*
11768  * - LGPL
11769  *
11770  * Input
11771  * 
11772  */
11773
11774 /**
11775  * @class Roo.bootstrap.Input
11776  * @extends Roo.bootstrap.Component
11777  * Bootstrap Input class
11778  * @cfg {Boolean} disabled is it disabled
11779  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11780  * @cfg {String} name name of the input
11781  * @cfg {string} fieldLabel - the label associated
11782  * @cfg {string} placeholder - placeholder to put in text.
11783  * @cfg {string}  before - input group add on before
11784  * @cfg {string} after - input group add on after
11785  * @cfg {string} size - (lg|sm) or leave empty..
11786  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11787  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11788  * @cfg {Number} md colspan out of 12 for computer-sized screens
11789  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11790  * @cfg {string} value default value of the input
11791  * @cfg {Number} labelWidth set the width of label 
11792  * @cfg {Number} labellg set the width of label (1-12)
11793  * @cfg {Number} labelmd set the width of label (1-12)
11794  * @cfg {Number} labelsm set the width of label (1-12)
11795  * @cfg {Number} labelxs set the width of label (1-12)
11796  * @cfg {String} labelAlign (top|left)
11797  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11798  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11799  * @cfg {String} indicatorpos (left|right) default left
11800  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11801  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11802  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11803
11804  * @cfg {String} align (left|center|right) Default left
11805  * @cfg {Boolean} forceFeedback (true|false) Default false
11806  * 
11807  * @constructor
11808  * Create a new Input
11809  * @param {Object} config The config object
11810  */
11811
11812 Roo.bootstrap.Input = function(config){
11813     
11814     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11815     
11816     this.addEvents({
11817         /**
11818          * @event focus
11819          * Fires when this field receives input focus.
11820          * @param {Roo.form.Field} this
11821          */
11822         focus : true,
11823         /**
11824          * @event blur
11825          * Fires when this field loses input focus.
11826          * @param {Roo.form.Field} this
11827          */
11828         blur : true,
11829         /**
11830          * @event specialkey
11831          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11832          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11833          * @param {Roo.form.Field} this
11834          * @param {Roo.EventObject} e The event object
11835          */
11836         specialkey : true,
11837         /**
11838          * @event change
11839          * Fires just before the field blurs if the field value has changed.
11840          * @param {Roo.form.Field} this
11841          * @param {Mixed} newValue The new value
11842          * @param {Mixed} oldValue The original value
11843          */
11844         change : true,
11845         /**
11846          * @event invalid
11847          * Fires after the field has been marked as invalid.
11848          * @param {Roo.form.Field} this
11849          * @param {String} msg The validation message
11850          */
11851         invalid : true,
11852         /**
11853          * @event valid
11854          * Fires after the field has been validated with no errors.
11855          * @param {Roo.form.Field} this
11856          */
11857         valid : true,
11858          /**
11859          * @event keyup
11860          * Fires after the key up
11861          * @param {Roo.form.Field} this
11862          * @param {Roo.EventObject}  e The event Object
11863          */
11864         keyup : true,
11865         /**
11866          * @event paste
11867          * Fires after the user pastes into input
11868          * @param {Roo.form.Field} this
11869          * @param {Roo.EventObject}  e The event Object
11870          */
11871         paste : true
11872     });
11873 };
11874
11875 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11876      /**
11877      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11878       automatic validation (defaults to "keyup").
11879      */
11880     validationEvent : "keyup",
11881      /**
11882      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11883      */
11884     validateOnBlur : true,
11885     /**
11886      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11887      */
11888     validationDelay : 250,
11889      /**
11890      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11891      */
11892     focusClass : "x-form-focus",  // not needed???
11893     
11894        
11895     /**
11896      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11897      */
11898     invalidClass : "has-warning",
11899     
11900     /**
11901      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11902      */
11903     validClass : "has-success",
11904     
11905     /**
11906      * @cfg {Boolean} hasFeedback (true|false) default true
11907      */
11908     hasFeedback : true,
11909     
11910     /**
11911      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11912      */
11913     invalidFeedbackClass : "glyphicon-warning-sign",
11914     
11915     /**
11916      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11917      */
11918     validFeedbackClass : "glyphicon-ok",
11919     
11920     /**
11921      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11922      */
11923     selectOnFocus : false,
11924     
11925      /**
11926      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11927      */
11928     maskRe : null,
11929        /**
11930      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11931      */
11932     vtype : null,
11933     
11934       /**
11935      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11936      */
11937     disableKeyFilter : false,
11938     
11939        /**
11940      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11941      */
11942     disabled : false,
11943      /**
11944      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11945      */
11946     allowBlank : true,
11947     /**
11948      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11949      */
11950     blankText : "Please complete this mandatory field",
11951     
11952      /**
11953      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11954      */
11955     minLength : 0,
11956     /**
11957      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11958      */
11959     maxLength : Number.MAX_VALUE,
11960     /**
11961      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11962      */
11963     minLengthText : "The minimum length for this field is {0}",
11964     /**
11965      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11966      */
11967     maxLengthText : "The maximum length for this field is {0}",
11968   
11969     
11970     /**
11971      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11972      * If available, this function will be called only after the basic validators all return true, and will be passed the
11973      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11974      */
11975     validator : null,
11976     /**
11977      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11978      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11979      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11980      */
11981     regex : null,
11982     /**
11983      * @cfg {String} regexText -- Depricated - use Invalid Text
11984      */
11985     regexText : "",
11986     
11987     /**
11988      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11989      */
11990     invalidText : "",
11991     
11992     
11993     
11994     autocomplete: false,
11995     
11996     
11997     fieldLabel : '',
11998     inputType : 'text',
11999     
12000     name : false,
12001     placeholder: false,
12002     before : false,
12003     after : false,
12004     size : false,
12005     hasFocus : false,
12006     preventMark: false,
12007     isFormField : true,
12008     value : '',
12009     labelWidth : 2,
12010     labelAlign : false,
12011     readOnly : false,
12012     align : false,
12013     formatedValue : false,
12014     forceFeedback : false,
12015     
12016     indicatorpos : 'left',
12017     
12018     labellg : 0,
12019     labelmd : 0,
12020     labelsm : 0,
12021     labelxs : 0,
12022     
12023     capture : '',
12024     accept : '',
12025     
12026     parentLabelAlign : function()
12027     {
12028         var parent = this;
12029         while (parent.parent()) {
12030             parent = parent.parent();
12031             if (typeof(parent.labelAlign) !='undefined') {
12032                 return parent.labelAlign;
12033             }
12034         }
12035         return 'left';
12036         
12037     },
12038     
12039     getAutoCreate : function()
12040     {
12041         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12042         
12043         var id = Roo.id();
12044         
12045         var cfg = {};
12046         
12047         if(this.inputType != 'hidden'){
12048             cfg.cls = 'form-group' //input-group
12049         }
12050         
12051         var input =  {
12052             tag: 'input',
12053             id : id,
12054             type : this.inputType,
12055             value : this.value,
12056             cls : 'form-control',
12057             placeholder : this.placeholder || '',
12058             autocomplete : this.autocomplete || 'new-password'
12059         };
12060         if (this.inputType == 'file') {
12061             input.style = 'overflow:hidden'; // why not in CSS?
12062         }
12063         
12064         if(this.capture.length){
12065             input.capture = this.capture;
12066         }
12067         
12068         if(this.accept.length){
12069             input.accept = this.accept + "/*";
12070         }
12071         
12072         if(this.align){
12073             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12074         }
12075         
12076         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12077             input.maxLength = this.maxLength;
12078         }
12079         
12080         if (this.disabled) {
12081             input.disabled=true;
12082         }
12083         
12084         if (this.readOnly) {
12085             input.readonly=true;
12086         }
12087         
12088         if (this.name) {
12089             input.name = this.name;
12090         }
12091         
12092         if (this.size) {
12093             input.cls += ' input-' + this.size;
12094         }
12095         
12096         var settings=this;
12097         ['xs','sm','md','lg'].map(function(size){
12098             if (settings[size]) {
12099                 cfg.cls += ' col-' + size + '-' + settings[size];
12100             }
12101         });
12102         
12103         var inputblock = input;
12104         
12105         var feedback = {
12106             tag: 'span',
12107             cls: 'glyphicon form-control-feedback'
12108         };
12109             
12110         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12111             
12112             inputblock = {
12113                 cls : 'has-feedback',
12114                 cn :  [
12115                     input,
12116                     feedback
12117                 ] 
12118             };  
12119         }
12120         
12121         if (this.before || this.after) {
12122             
12123             inputblock = {
12124                 cls : 'input-group',
12125                 cn :  [] 
12126             };
12127             
12128             if (this.before && typeof(this.before) == 'string') {
12129                 
12130                 inputblock.cn.push({
12131                     tag :'span',
12132                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12133                     html : this.before
12134                 });
12135             }
12136             if (this.before && typeof(this.before) == 'object') {
12137                 this.before = Roo.factory(this.before);
12138                 
12139                 inputblock.cn.push({
12140                     tag :'span',
12141                     cls : 'roo-input-before input-group-prepend   input-group-' +
12142                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12143                 });
12144             }
12145             
12146             inputblock.cn.push(input);
12147             
12148             if (this.after && typeof(this.after) == 'string') {
12149                 inputblock.cn.push({
12150                     tag :'span',
12151                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12152                     html : this.after
12153                 });
12154             }
12155             if (this.after && typeof(this.after) == 'object') {
12156                 this.after = Roo.factory(this.after);
12157                 
12158                 inputblock.cn.push({
12159                     tag :'span',
12160                     cls : 'roo-input-after input-group-append  input-group-' +
12161                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12162                 });
12163             }
12164             
12165             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12166                 inputblock.cls += ' has-feedback';
12167                 inputblock.cn.push(feedback);
12168             }
12169         };
12170         var indicator = {
12171             tag : 'i',
12172             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12173             tooltip : 'This field is required'
12174         };
12175         if (this.allowBlank ) {
12176             indicator.style = this.allowBlank ? ' display:none' : '';
12177         }
12178         if (align ==='left' && this.fieldLabel.length) {
12179             
12180             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12181             
12182             cfg.cn = [
12183                 indicator,
12184                 {
12185                     tag: 'label',
12186                     'for' :  id,
12187                     cls : 'control-label col-form-label',
12188                     html : this.fieldLabel
12189
12190                 },
12191                 {
12192                     cls : "", 
12193                     cn: [
12194                         inputblock
12195                     ]
12196                 }
12197             ];
12198             
12199             var labelCfg = cfg.cn[1];
12200             var contentCfg = cfg.cn[2];
12201             
12202             if(this.indicatorpos == 'right'){
12203                 cfg.cn = [
12204                     {
12205                         tag: 'label',
12206                         'for' :  id,
12207                         cls : 'control-label col-form-label',
12208                         cn : [
12209                             {
12210                                 tag : 'span',
12211                                 html : this.fieldLabel
12212                             },
12213                             indicator
12214                         ]
12215                     },
12216                     {
12217                         cls : "",
12218                         cn: [
12219                             inputblock
12220                         ]
12221                     }
12222
12223                 ];
12224                 
12225                 labelCfg = cfg.cn[0];
12226                 contentCfg = cfg.cn[1];
12227             
12228             }
12229             
12230             if(this.labelWidth > 12){
12231                 labelCfg.style = "width: " + this.labelWidth + 'px';
12232             }
12233             
12234             if(this.labelWidth < 13 && this.labelmd == 0){
12235                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12236             }
12237             
12238             if(this.labellg > 0){
12239                 labelCfg.cls += ' col-lg-' + this.labellg;
12240                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12241             }
12242             
12243             if(this.labelmd > 0){
12244                 labelCfg.cls += ' col-md-' + this.labelmd;
12245                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12246             }
12247             
12248             if(this.labelsm > 0){
12249                 labelCfg.cls += ' col-sm-' + this.labelsm;
12250                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12251             }
12252             
12253             if(this.labelxs > 0){
12254                 labelCfg.cls += ' col-xs-' + this.labelxs;
12255                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12256             }
12257             
12258             
12259         } else if ( this.fieldLabel.length) {
12260                 
12261             
12262             
12263             cfg.cn = [
12264                 {
12265                     tag : 'i',
12266                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12267                     tooltip : 'This field is required',
12268                     style : this.allowBlank ? ' display:none' : '' 
12269                 },
12270                 {
12271                     tag: 'label',
12272                    //cls : 'input-group-addon',
12273                     html : this.fieldLabel
12274
12275                 },
12276
12277                inputblock
12278
12279            ];
12280            
12281            if(this.indicatorpos == 'right'){
12282        
12283                 cfg.cn = [
12284                     {
12285                         tag: 'label',
12286                        //cls : 'input-group-addon',
12287                         html : this.fieldLabel
12288
12289                     },
12290                     {
12291                         tag : 'i',
12292                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12293                         tooltip : 'This field is required',
12294                         style : this.allowBlank ? ' display:none' : '' 
12295                     },
12296
12297                    inputblock
12298
12299                ];
12300
12301             }
12302
12303         } else {
12304             
12305             cfg.cn = [
12306
12307                     inputblock
12308
12309             ];
12310                 
12311                 
12312         };
12313         
12314         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12315            cfg.cls += ' navbar-form';
12316         }
12317         
12318         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12319             // on BS4 we do this only if not form 
12320             cfg.cls += ' navbar-form';
12321             cfg.tag = 'li';
12322         }
12323         
12324         return cfg;
12325         
12326     },
12327     /**
12328      * return the real input element.
12329      */
12330     inputEl: function ()
12331     {
12332         return this.el.select('input.form-control',true).first();
12333     },
12334     
12335     tooltipEl : function()
12336     {
12337         return this.inputEl();
12338     },
12339     
12340     indicatorEl : function()
12341     {
12342         if (Roo.bootstrap.version == 4) {
12343             return false; // not enabled in v4 yet.
12344         }
12345         
12346         var indicator = this.el.select('i.roo-required-indicator',true).first();
12347         
12348         if(!indicator){
12349             return false;
12350         }
12351         
12352         return indicator;
12353         
12354     },
12355     
12356     setDisabled : function(v)
12357     {
12358         var i  = this.inputEl().dom;
12359         if (!v) {
12360             i.removeAttribute('disabled');
12361             return;
12362             
12363         }
12364         i.setAttribute('disabled','true');
12365     },
12366     initEvents : function()
12367     {
12368           
12369         this.inputEl().on("keydown" , this.fireKey,  this);
12370         this.inputEl().on("focus", this.onFocus,  this);
12371         this.inputEl().on("blur", this.onBlur,  this);
12372         
12373         this.inputEl().relayEvent('keyup', this);
12374         this.inputEl().relayEvent('paste', this);
12375         
12376         this.indicator = this.indicatorEl();
12377         
12378         if(this.indicator){
12379             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12380         }
12381  
12382         // reference to original value for reset
12383         this.originalValue = this.getValue();
12384         //Roo.form.TextField.superclass.initEvents.call(this);
12385         if(this.validationEvent == 'keyup'){
12386             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12387             this.inputEl().on('keyup', this.filterValidation, this);
12388         }
12389         else if(this.validationEvent !== false){
12390             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12391         }
12392         
12393         if(this.selectOnFocus){
12394             this.on("focus", this.preFocus, this);
12395             
12396         }
12397         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12398             this.inputEl().on("keypress", this.filterKeys, this);
12399         } else {
12400             this.inputEl().relayEvent('keypress', this);
12401         }
12402        /* if(this.grow){
12403             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12404             this.el.on("click", this.autoSize,  this);
12405         }
12406         */
12407         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12408             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12409         }
12410         
12411         if (typeof(this.before) == 'object') {
12412             this.before.render(this.el.select('.roo-input-before',true).first());
12413         }
12414         if (typeof(this.after) == 'object') {
12415             this.after.render(this.el.select('.roo-input-after',true).first());
12416         }
12417         
12418         this.inputEl().on('change', this.onChange, this);
12419         
12420     },
12421     filterValidation : function(e){
12422         if(!e.isNavKeyPress()){
12423             this.validationTask.delay(this.validationDelay);
12424         }
12425     },
12426      /**
12427      * Validates the field value
12428      * @return {Boolean} True if the value is valid, else false
12429      */
12430     validate : function(){
12431         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12432         if(this.disabled || this.validateValue(this.getRawValue())){
12433             this.markValid();
12434             return true;
12435         }
12436         
12437         this.markInvalid();
12438         return false;
12439     },
12440     
12441     
12442     /**
12443      * Validates a value according to the field's validation rules and marks the field as invalid
12444      * if the validation fails
12445      * @param {Mixed} value The value to validate
12446      * @return {Boolean} True if the value is valid, else false
12447      */
12448     validateValue : function(value)
12449     {
12450         if(this.getVisibilityEl().hasClass('hidden')){
12451             return true;
12452         }
12453         
12454         if(value.length < 1)  { // if it's blank
12455             if(this.allowBlank){
12456                 return true;
12457             }
12458             return false;
12459         }
12460         
12461         if(value.length < this.minLength){
12462             return false;
12463         }
12464         if(value.length > this.maxLength){
12465             return false;
12466         }
12467         if(this.vtype){
12468             var vt = Roo.form.VTypes;
12469             if(!vt[this.vtype](value, this)){
12470                 return false;
12471             }
12472         }
12473         if(typeof this.validator == "function"){
12474             var msg = this.validator(value);
12475             if(msg !== true){
12476                 return false;
12477             }
12478             if (typeof(msg) == 'string') {
12479                 this.invalidText = msg;
12480             }
12481         }
12482         
12483         if(this.regex && !this.regex.test(value)){
12484             return false;
12485         }
12486         
12487         return true;
12488     },
12489     
12490      // private
12491     fireKey : function(e){
12492         //Roo.log('field ' + e.getKey());
12493         if(e.isNavKeyPress()){
12494             this.fireEvent("specialkey", this, e);
12495         }
12496     },
12497     focus : function (selectText){
12498         if(this.rendered){
12499             this.inputEl().focus();
12500             if(selectText === true){
12501                 this.inputEl().dom.select();
12502             }
12503         }
12504         return this;
12505     } ,
12506     
12507     onFocus : function(){
12508         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12509            // this.el.addClass(this.focusClass);
12510         }
12511         if(!this.hasFocus){
12512             this.hasFocus = true;
12513             this.startValue = this.getValue();
12514             this.fireEvent("focus", this);
12515         }
12516     },
12517     
12518     beforeBlur : Roo.emptyFn,
12519
12520     
12521     // private
12522     onBlur : function(){
12523         this.beforeBlur();
12524         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12525             //this.el.removeClass(this.focusClass);
12526         }
12527         this.hasFocus = false;
12528         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12529             this.validate();
12530         }
12531         var v = this.getValue();
12532         if(String(v) !== String(this.startValue)){
12533             this.fireEvent('change', this, v, this.startValue);
12534         }
12535         this.fireEvent("blur", this);
12536     },
12537     
12538     onChange : function(e)
12539     {
12540         var v = this.getValue();
12541         if(String(v) !== String(this.startValue)){
12542             this.fireEvent('change', this, v, this.startValue);
12543         }
12544         
12545     },
12546     
12547     /**
12548      * Resets the current field value to the originally loaded value and clears any validation messages
12549      */
12550     reset : function(){
12551         this.setValue(this.originalValue);
12552         this.validate();
12553     },
12554      /**
12555      * Returns the name of the field
12556      * @return {Mixed} name The name field
12557      */
12558     getName: function(){
12559         return this.name;
12560     },
12561      /**
12562      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12563      * @return {Mixed} value The field value
12564      */
12565     getValue : function(){
12566         
12567         var v = this.inputEl().getValue();
12568         
12569         return v;
12570     },
12571     /**
12572      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12573      * @return {Mixed} value The field value
12574      */
12575     getRawValue : function(){
12576         var v = this.inputEl().getValue();
12577         
12578         return v;
12579     },
12580     
12581     /**
12582      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12583      * @param {Mixed} value The value to set
12584      */
12585     setRawValue : function(v){
12586         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12587     },
12588     
12589     selectText : function(start, end){
12590         var v = this.getRawValue();
12591         if(v.length > 0){
12592             start = start === undefined ? 0 : start;
12593             end = end === undefined ? v.length : end;
12594             var d = this.inputEl().dom;
12595             if(d.setSelectionRange){
12596                 d.setSelectionRange(start, end);
12597             }else if(d.createTextRange){
12598                 var range = d.createTextRange();
12599                 range.moveStart("character", start);
12600                 range.moveEnd("character", v.length-end);
12601                 range.select();
12602             }
12603         }
12604     },
12605     
12606     /**
12607      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12608      * @param {Mixed} value The value to set
12609      */
12610     setValue : function(v){
12611         this.value = v;
12612         if(this.rendered){
12613             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12614             this.validate();
12615         }
12616     },
12617     
12618     /*
12619     processValue : function(value){
12620         if(this.stripCharsRe){
12621             var newValue = value.replace(this.stripCharsRe, '');
12622             if(newValue !== value){
12623                 this.setRawValue(newValue);
12624                 return newValue;
12625             }
12626         }
12627         return value;
12628     },
12629   */
12630     preFocus : function(){
12631         
12632         if(this.selectOnFocus){
12633             this.inputEl().dom.select();
12634         }
12635     },
12636     filterKeys : function(e){
12637         var k = e.getKey();
12638         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12639             return;
12640         }
12641         var c = e.getCharCode(), cc = String.fromCharCode(c);
12642         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12643             return;
12644         }
12645         if(!this.maskRe.test(cc)){
12646             e.stopEvent();
12647         }
12648     },
12649      /**
12650      * Clear any invalid styles/messages for this field
12651      */
12652     clearInvalid : function(){
12653         
12654         if(!this.el || this.preventMark){ // not rendered
12655             return;
12656         }
12657         
12658         
12659         this.el.removeClass([this.invalidClass, 'is-invalid']);
12660         
12661         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12662             
12663             var feedback = this.el.select('.form-control-feedback', true).first();
12664             
12665             if(feedback){
12666                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12667             }
12668             
12669         }
12670         
12671         if(this.indicator){
12672             this.indicator.removeClass('visible');
12673             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12674         }
12675         
12676         this.fireEvent('valid', this);
12677     },
12678     
12679      /**
12680      * Mark this field as valid
12681      */
12682     markValid : function()
12683     {
12684         if(!this.el  || this.preventMark){ // not rendered...
12685             return;
12686         }
12687         
12688         this.el.removeClass([this.invalidClass, this.validClass]);
12689         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12690
12691         var feedback = this.el.select('.form-control-feedback', true).first();
12692             
12693         if(feedback){
12694             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12695         }
12696         
12697         if(this.indicator){
12698             this.indicator.removeClass('visible');
12699             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12700         }
12701         
12702         if(this.disabled){
12703             return;
12704         }
12705         
12706            
12707         if(this.allowBlank && !this.getRawValue().length){
12708             return;
12709         }
12710         if (Roo.bootstrap.version == 3) {
12711             this.el.addClass(this.validClass);
12712         } else {
12713             this.inputEl().addClass('is-valid');
12714         }
12715
12716         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12717             
12718             var feedback = this.el.select('.form-control-feedback', true).first();
12719             
12720             if(feedback){
12721                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12722                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12723             }
12724             
12725         }
12726         
12727         this.fireEvent('valid', this);
12728     },
12729     
12730      /**
12731      * Mark this field as invalid
12732      * @param {String} msg The validation message
12733      */
12734     markInvalid : function(msg)
12735     {
12736         if(!this.el  || this.preventMark){ // not rendered
12737             return;
12738         }
12739         
12740         this.el.removeClass([this.invalidClass, this.validClass]);
12741         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12742         
12743         var feedback = this.el.select('.form-control-feedback', true).first();
12744             
12745         if(feedback){
12746             this.el.select('.form-control-feedback', true).first().removeClass(
12747                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12748         }
12749
12750         if(this.disabled){
12751             return;
12752         }
12753         
12754         if(this.allowBlank && !this.getRawValue().length){
12755             return;
12756         }
12757         
12758         if(this.indicator){
12759             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12760             this.indicator.addClass('visible');
12761         }
12762         if (Roo.bootstrap.version == 3) {
12763             this.el.addClass(this.invalidClass);
12764         } else {
12765             this.inputEl().addClass('is-invalid');
12766         }
12767         
12768         
12769         
12770         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12771             
12772             var feedback = this.el.select('.form-control-feedback', true).first();
12773             
12774             if(feedback){
12775                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12776                 
12777                 if(this.getValue().length || this.forceFeedback){
12778                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12779                 }
12780                 
12781             }
12782             
12783         }
12784         
12785         this.fireEvent('invalid', this, msg);
12786     },
12787     // private
12788     SafariOnKeyDown : function(event)
12789     {
12790         // this is a workaround for a password hang bug on chrome/ webkit.
12791         if (this.inputEl().dom.type != 'password') {
12792             return;
12793         }
12794         
12795         var isSelectAll = false;
12796         
12797         if(this.inputEl().dom.selectionEnd > 0){
12798             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12799         }
12800         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12801             event.preventDefault();
12802             this.setValue('');
12803             return;
12804         }
12805         
12806         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12807             
12808             event.preventDefault();
12809             // this is very hacky as keydown always get's upper case.
12810             //
12811             var cc = String.fromCharCode(event.getCharCode());
12812             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12813             
12814         }
12815     },
12816     adjustWidth : function(tag, w){
12817         tag = tag.toLowerCase();
12818         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12819             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12820                 if(tag == 'input'){
12821                     return w + 2;
12822                 }
12823                 if(tag == 'textarea'){
12824                     return w-2;
12825                 }
12826             }else if(Roo.isOpera){
12827                 if(tag == 'input'){
12828                     return w + 2;
12829                 }
12830                 if(tag == 'textarea'){
12831                     return w-2;
12832                 }
12833             }
12834         }
12835         return w;
12836     },
12837     
12838     setFieldLabel : function(v)
12839     {
12840         if(!this.rendered){
12841             return;
12842         }
12843         
12844         if(this.indicatorEl()){
12845             var ar = this.el.select('label > span',true);
12846             
12847             if (ar.elements.length) {
12848                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12849                 this.fieldLabel = v;
12850                 return;
12851             }
12852             
12853             var br = this.el.select('label',true);
12854             
12855             if(br.elements.length) {
12856                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12857                 this.fieldLabel = v;
12858                 return;
12859             }
12860             
12861             Roo.log('Cannot Found any of label > span || label in input');
12862             return;
12863         }
12864         
12865         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12866         this.fieldLabel = v;
12867         
12868         
12869     }
12870 });
12871
12872  
12873 /*
12874  * - LGPL
12875  *
12876  * Input
12877  * 
12878  */
12879
12880 /**
12881  * @class Roo.bootstrap.TextArea
12882  * @extends Roo.bootstrap.Input
12883  * Bootstrap TextArea class
12884  * @cfg {Number} cols Specifies the visible width of a text area
12885  * @cfg {Number} rows Specifies the visible number of lines in a text area
12886  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12887  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12888  * @cfg {string} html text
12889  * 
12890  * @constructor
12891  * Create a new TextArea
12892  * @param {Object} config The config object
12893  */
12894
12895 Roo.bootstrap.TextArea = function(config){
12896     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12897    
12898 };
12899
12900 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12901      
12902     cols : false,
12903     rows : 5,
12904     readOnly : false,
12905     warp : 'soft',
12906     resize : false,
12907     value: false,
12908     html: false,
12909     
12910     getAutoCreate : function(){
12911         
12912         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12913         
12914         var id = Roo.id();
12915         
12916         var cfg = {};
12917         
12918         if(this.inputType != 'hidden'){
12919             cfg.cls = 'form-group' //input-group
12920         }
12921         
12922         var input =  {
12923             tag: 'textarea',
12924             id : id,
12925             warp : this.warp,
12926             rows : this.rows,
12927             value : this.value || '',
12928             html: this.html || '',
12929             cls : 'form-control',
12930             placeholder : this.placeholder || '' 
12931             
12932         };
12933         
12934         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12935             input.maxLength = this.maxLength;
12936         }
12937         
12938         if(this.resize){
12939             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12940         }
12941         
12942         if(this.cols){
12943             input.cols = this.cols;
12944         }
12945         
12946         if (this.readOnly) {
12947             input.readonly = true;
12948         }
12949         
12950         if (this.name) {
12951             input.name = this.name;
12952         }
12953         
12954         if (this.size) {
12955             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12956         }
12957         
12958         var settings=this;
12959         ['xs','sm','md','lg'].map(function(size){
12960             if (settings[size]) {
12961                 cfg.cls += ' col-' + size + '-' + settings[size];
12962             }
12963         });
12964         
12965         var inputblock = input;
12966         
12967         if(this.hasFeedback && !this.allowBlank){
12968             
12969             var feedback = {
12970                 tag: 'span',
12971                 cls: 'glyphicon form-control-feedback'
12972             };
12973
12974             inputblock = {
12975                 cls : 'has-feedback',
12976                 cn :  [
12977                     input,
12978                     feedback
12979                 ] 
12980             };  
12981         }
12982         
12983         
12984         if (this.before || this.after) {
12985             
12986             inputblock = {
12987                 cls : 'input-group',
12988                 cn :  [] 
12989             };
12990             if (this.before) {
12991                 inputblock.cn.push({
12992                     tag :'span',
12993                     cls : 'input-group-addon',
12994                     html : this.before
12995                 });
12996             }
12997             
12998             inputblock.cn.push(input);
12999             
13000             if(this.hasFeedback && !this.allowBlank){
13001                 inputblock.cls += ' has-feedback';
13002                 inputblock.cn.push(feedback);
13003             }
13004             
13005             if (this.after) {
13006                 inputblock.cn.push({
13007                     tag :'span',
13008                     cls : 'input-group-addon',
13009                     html : this.after
13010                 });
13011             }
13012             
13013         }
13014         
13015         if (align ==='left' && this.fieldLabel.length) {
13016             cfg.cn = [
13017                 {
13018                     tag: 'label',
13019                     'for' :  id,
13020                     cls : 'control-label',
13021                     html : this.fieldLabel
13022                 },
13023                 {
13024                     cls : "",
13025                     cn: [
13026                         inputblock
13027                     ]
13028                 }
13029
13030             ];
13031             
13032             if(this.labelWidth > 12){
13033                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13034             }
13035
13036             if(this.labelWidth < 13 && this.labelmd == 0){
13037                 this.labelmd = this.labelWidth;
13038             }
13039
13040             if(this.labellg > 0){
13041                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13042                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13043             }
13044
13045             if(this.labelmd > 0){
13046                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13047                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13048             }
13049
13050             if(this.labelsm > 0){
13051                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13052                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13053             }
13054
13055             if(this.labelxs > 0){
13056                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13057                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13058             }
13059             
13060         } else if ( this.fieldLabel.length) {
13061             cfg.cn = [
13062
13063                {
13064                    tag: 'label',
13065                    //cls : 'input-group-addon',
13066                    html : this.fieldLabel
13067
13068                },
13069
13070                inputblock
13071
13072            ];
13073
13074         } else {
13075
13076             cfg.cn = [
13077
13078                 inputblock
13079
13080             ];
13081                 
13082         }
13083         
13084         if (this.disabled) {
13085             input.disabled=true;
13086         }
13087         
13088         return cfg;
13089         
13090     },
13091     /**
13092      * return the real textarea element.
13093      */
13094     inputEl: function ()
13095     {
13096         return this.el.select('textarea.form-control',true).first();
13097     },
13098     
13099     /**
13100      * Clear any invalid styles/messages for this field
13101      */
13102     clearInvalid : function()
13103     {
13104         
13105         if(!this.el || this.preventMark){ // not rendered
13106             return;
13107         }
13108         
13109         var label = this.el.select('label', true).first();
13110         var icon = this.el.select('i.fa-star', true).first();
13111         
13112         if(label && icon){
13113             icon.remove();
13114         }
13115         this.el.removeClass( this.validClass);
13116         this.inputEl().removeClass('is-invalid');
13117          
13118         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13119             
13120             var feedback = this.el.select('.form-control-feedback', true).first();
13121             
13122             if(feedback){
13123                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13124             }
13125             
13126         }
13127         
13128         this.fireEvent('valid', this);
13129     },
13130     
13131      /**
13132      * Mark this field as valid
13133      */
13134     markValid : function()
13135     {
13136         if(!this.el  || this.preventMark){ // not rendered
13137             return;
13138         }
13139         
13140         this.el.removeClass([this.invalidClass, this.validClass]);
13141         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13142         
13143         var feedback = this.el.select('.form-control-feedback', true).first();
13144             
13145         if(feedback){
13146             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13147         }
13148
13149         if(this.disabled || this.allowBlank){
13150             return;
13151         }
13152         
13153         var label = this.el.select('label', true).first();
13154         var icon = this.el.select('i.fa-star', true).first();
13155         
13156         if(label && icon){
13157             icon.remove();
13158         }
13159         if (Roo.bootstrap.version == 3) {
13160             this.el.addClass(this.validClass);
13161         } else {
13162             this.inputEl().addClass('is-valid');
13163         }
13164         
13165         
13166         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13167             
13168             var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170             if(feedback){
13171                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13173             }
13174             
13175         }
13176         
13177         this.fireEvent('valid', this);
13178     },
13179     
13180      /**
13181      * Mark this field as invalid
13182      * @param {String} msg The validation message
13183      */
13184     markInvalid : function(msg)
13185     {
13186         if(!this.el  || this.preventMark){ // not rendered
13187             return;
13188         }
13189         
13190         this.el.removeClass([this.invalidClass, this.validClass]);
13191         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13192         
13193         var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195         if(feedback){
13196             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13197         }
13198
13199         if(this.disabled || this.allowBlank){
13200             return;
13201         }
13202         
13203         var label = this.el.select('label', true).first();
13204         var icon = this.el.select('i.fa-star', true).first();
13205         
13206         if(!this.getValue().length && label && !icon){
13207             this.el.createChild({
13208                 tag : 'i',
13209                 cls : 'text-danger fa fa-lg fa-star',
13210                 tooltip : 'This field is required',
13211                 style : 'margin-right:5px;'
13212             }, label, true);
13213         }
13214         
13215         if (Roo.bootstrap.version == 3) {
13216             this.el.addClass(this.invalidClass);
13217         } else {
13218             this.inputEl().addClass('is-invalid');
13219         }
13220         
13221         // fixme ... this may be depricated need to test..
13222         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13223             
13224             var feedback = this.el.select('.form-control-feedback', true).first();
13225             
13226             if(feedback){
13227                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13228                 
13229                 if(this.getValue().length || this.forceFeedback){
13230                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13231                 }
13232                 
13233             }
13234             
13235         }
13236         
13237         this.fireEvent('invalid', this, msg);
13238     }
13239 });
13240
13241  
13242 /*
13243  * - LGPL
13244  *
13245  * trigger field - base class for combo..
13246  * 
13247  */
13248  
13249 /**
13250  * @class Roo.bootstrap.TriggerField
13251  * @extends Roo.bootstrap.Input
13252  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13253  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13254  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13255  * for which you can provide a custom implementation.  For example:
13256  * <pre><code>
13257 var trigger = new Roo.bootstrap.TriggerField();
13258 trigger.onTriggerClick = myTriggerFn;
13259 trigger.applyTo('my-field');
13260 </code></pre>
13261  *
13262  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13263  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13264  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13265  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13266  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13267
13268  * @constructor
13269  * Create a new TriggerField.
13270  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13271  * to the base TextField)
13272  */
13273 Roo.bootstrap.TriggerField = function(config){
13274     this.mimicing = false;
13275     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13276 };
13277
13278 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13279     /**
13280      * @cfg {String} triggerClass A CSS class to apply to the trigger
13281      */
13282      /**
13283      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13284      */
13285     hideTrigger:false,
13286
13287     /**
13288      * @cfg {Boolean} removable (true|false) special filter default false
13289      */
13290     removable : false,
13291     
13292     /** @cfg {Boolean} grow @hide */
13293     /** @cfg {Number} growMin @hide */
13294     /** @cfg {Number} growMax @hide */
13295
13296     /**
13297      * @hide 
13298      * @method
13299      */
13300     autoSize: Roo.emptyFn,
13301     // private
13302     monitorTab : true,
13303     // private
13304     deferHeight : true,
13305
13306     
13307     actionMode : 'wrap',
13308     
13309     caret : false,
13310     
13311     
13312     getAutoCreate : function(){
13313        
13314         var align = this.labelAlign || this.parentLabelAlign();
13315         
13316         var id = Roo.id();
13317         
13318         var cfg = {
13319             cls: 'form-group' //input-group
13320         };
13321         
13322         
13323         var input =  {
13324             tag: 'input',
13325             id : id,
13326             type : this.inputType,
13327             cls : 'form-control',
13328             autocomplete: 'new-password',
13329             placeholder : this.placeholder || '' 
13330             
13331         };
13332         if (this.name) {
13333             input.name = this.name;
13334         }
13335         if (this.size) {
13336             input.cls += ' input-' + this.size;
13337         }
13338         
13339         if (this.disabled) {
13340             input.disabled=true;
13341         }
13342         
13343         var inputblock = input;
13344         
13345         if(this.hasFeedback && !this.allowBlank){
13346             
13347             var feedback = {
13348                 tag: 'span',
13349                 cls: 'glyphicon form-control-feedback'
13350             };
13351             
13352             if(this.removable && !this.editable  ){
13353                 inputblock = {
13354                     cls : 'has-feedback',
13355                     cn :  [
13356                         inputblock,
13357                         {
13358                             tag: 'button',
13359                             html : 'x',
13360                             cls : 'roo-combo-removable-btn close'
13361                         },
13362                         feedback
13363                     ] 
13364                 };
13365             } else {
13366                 inputblock = {
13367                     cls : 'has-feedback',
13368                     cn :  [
13369                         inputblock,
13370                         feedback
13371                     ] 
13372                 };
13373             }
13374
13375         } else {
13376             if(this.removable && !this.editable ){
13377                 inputblock = {
13378                     cls : 'roo-removable',
13379                     cn :  [
13380                         inputblock,
13381                         {
13382                             tag: 'button',
13383                             html : 'x',
13384                             cls : 'roo-combo-removable-btn close'
13385                         }
13386                     ] 
13387                 };
13388             }
13389         }
13390         
13391         if (this.before || this.after) {
13392             
13393             inputblock = {
13394                 cls : 'input-group',
13395                 cn :  [] 
13396             };
13397             if (this.before) {
13398                 inputblock.cn.push({
13399                     tag :'span',
13400                     cls : 'input-group-addon input-group-prepend input-group-text',
13401                     html : this.before
13402                 });
13403             }
13404             
13405             inputblock.cn.push(input);
13406             
13407             if(this.hasFeedback && !this.allowBlank){
13408                 inputblock.cls += ' has-feedback';
13409                 inputblock.cn.push(feedback);
13410             }
13411             
13412             if (this.after) {
13413                 inputblock.cn.push({
13414                     tag :'span',
13415                     cls : 'input-group-addon input-group-append input-group-text',
13416                     html : this.after
13417                 });
13418             }
13419             
13420         };
13421         
13422       
13423         
13424         var ibwrap = inputblock;
13425         
13426         if(this.multiple){
13427             ibwrap = {
13428                 tag: 'ul',
13429                 cls: 'roo-select2-choices',
13430                 cn:[
13431                     {
13432                         tag: 'li',
13433                         cls: 'roo-select2-search-field',
13434                         cn: [
13435
13436                             inputblock
13437                         ]
13438                     }
13439                 ]
13440             };
13441                 
13442         }
13443         
13444         var combobox = {
13445             cls: 'roo-select2-container input-group',
13446             cn: [
13447                  {
13448                     tag: 'input',
13449                     type : 'hidden',
13450                     cls: 'form-hidden-field'
13451                 },
13452                 ibwrap
13453             ]
13454         };
13455         
13456         if(!this.multiple && this.showToggleBtn){
13457             
13458             var caret = {
13459                         tag: 'span',
13460                         cls: 'caret'
13461              };
13462             if (this.caret != false) {
13463                 caret = {
13464                      tag: 'i',
13465                      cls: 'fa fa-' + this.caret
13466                 };
13467                 
13468             }
13469             
13470             combobox.cn.push({
13471                 tag :'span',
13472                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13473                 cn : [
13474                     Roo.bootstrap.version == 3 ? caret : '',
13475                     {
13476                         tag: 'span',
13477                         cls: 'combobox-clear',
13478                         cn  : [
13479                             {
13480                                 tag : 'i',
13481                                 cls: 'icon-remove'
13482                             }
13483                         ]
13484                     }
13485                 ]
13486
13487             })
13488         }
13489         
13490         if(this.multiple){
13491             combobox.cls += ' roo-select2-container-multi';
13492         }
13493          var indicator = {
13494             tag : 'i',
13495             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13496             tooltip : 'This field is required'
13497         };
13498         if (Roo.bootstrap.version == 4) {
13499             indicator = {
13500                 tag : 'i',
13501                 style : 'display:none'
13502             };
13503         }
13504         
13505         
13506         if (align ==='left' && this.fieldLabel.length) {
13507             
13508             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13509
13510             cfg.cn = [
13511                 indicator,
13512                 {
13513                     tag: 'label',
13514                     'for' :  id,
13515                     cls : 'control-label',
13516                     html : this.fieldLabel
13517
13518                 },
13519                 {
13520                     cls : "", 
13521                     cn: [
13522                         combobox
13523                     ]
13524                 }
13525
13526             ];
13527             
13528             var labelCfg = cfg.cn[1];
13529             var contentCfg = cfg.cn[2];
13530             
13531             if(this.indicatorpos == 'right'){
13532                 cfg.cn = [
13533                     {
13534                         tag: 'label',
13535                         'for' :  id,
13536                         cls : 'control-label',
13537                         cn : [
13538                             {
13539                                 tag : 'span',
13540                                 html : this.fieldLabel
13541                             },
13542                             indicator
13543                         ]
13544                     },
13545                     {
13546                         cls : "", 
13547                         cn: [
13548                             combobox
13549                         ]
13550                     }
13551
13552                 ];
13553                 
13554                 labelCfg = cfg.cn[0];
13555                 contentCfg = cfg.cn[1];
13556             }
13557             
13558             if(this.labelWidth > 12){
13559                 labelCfg.style = "width: " + this.labelWidth + 'px';
13560             }
13561             
13562             if(this.labelWidth < 13 && this.labelmd == 0){
13563                 this.labelmd = this.labelWidth;
13564             }
13565             
13566             if(this.labellg > 0){
13567                 labelCfg.cls += ' col-lg-' + this.labellg;
13568                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13569             }
13570             
13571             if(this.labelmd > 0){
13572                 labelCfg.cls += ' col-md-' + this.labelmd;
13573                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13574             }
13575             
13576             if(this.labelsm > 0){
13577                 labelCfg.cls += ' col-sm-' + this.labelsm;
13578                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13579             }
13580             
13581             if(this.labelxs > 0){
13582                 labelCfg.cls += ' col-xs-' + this.labelxs;
13583                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13584             }
13585             
13586         } else if ( this.fieldLabel.length) {
13587 //                Roo.log(" label");
13588             cfg.cn = [
13589                 indicator,
13590                {
13591                    tag: 'label',
13592                    //cls : 'input-group-addon',
13593                    html : this.fieldLabel
13594
13595                },
13596
13597                combobox
13598
13599             ];
13600             
13601             if(this.indicatorpos == 'right'){
13602                 
13603                 cfg.cn = [
13604                     {
13605                        tag: 'label',
13606                        cn : [
13607                            {
13608                                tag : 'span',
13609                                html : this.fieldLabel
13610                            },
13611                            indicator
13612                        ]
13613
13614                     },
13615                     combobox
13616
13617                 ];
13618
13619             }
13620
13621         } else {
13622             
13623 //                Roo.log(" no label && no align");
13624                 cfg = combobox
13625                      
13626                 
13627         }
13628         
13629         var settings=this;
13630         ['xs','sm','md','lg'].map(function(size){
13631             if (settings[size]) {
13632                 cfg.cls += ' col-' + size + '-' + settings[size];
13633             }
13634         });
13635         
13636         return cfg;
13637         
13638     },
13639     
13640     
13641     
13642     // private
13643     onResize : function(w, h){
13644 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13645 //        if(typeof w == 'number'){
13646 //            var x = w - this.trigger.getWidth();
13647 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13648 //            this.trigger.setStyle('left', x+'px');
13649 //        }
13650     },
13651
13652     // private
13653     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13654
13655     // private
13656     getResizeEl : function(){
13657         return this.inputEl();
13658     },
13659
13660     // private
13661     getPositionEl : function(){
13662         return this.inputEl();
13663     },
13664
13665     // private
13666     alignErrorIcon : function(){
13667         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13668     },
13669
13670     // private
13671     initEvents : function(){
13672         
13673         this.createList();
13674         
13675         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13676         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13677         if(!this.multiple && this.showToggleBtn){
13678             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13679             if(this.hideTrigger){
13680                 this.trigger.setDisplayed(false);
13681             }
13682             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13683         }
13684         
13685         if(this.multiple){
13686             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13687         }
13688         
13689         if(this.removable && !this.editable && !this.tickable){
13690             var close = this.closeTriggerEl();
13691             
13692             if(close){
13693                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13694                 close.on('click', this.removeBtnClick, this, close);
13695             }
13696         }
13697         
13698         //this.trigger.addClassOnOver('x-form-trigger-over');
13699         //this.trigger.addClassOnClick('x-form-trigger-click');
13700         
13701         //if(!this.width){
13702         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13703         //}
13704     },
13705     
13706     closeTriggerEl : function()
13707     {
13708         var close = this.el.select('.roo-combo-removable-btn', true).first();
13709         return close ? close : false;
13710     },
13711     
13712     removeBtnClick : function(e, h, el)
13713     {
13714         e.preventDefault();
13715         
13716         if(this.fireEvent("remove", this) !== false){
13717             this.reset();
13718             this.fireEvent("afterremove", this)
13719         }
13720     },
13721     
13722     createList : function()
13723     {
13724         this.list = Roo.get(document.body).createChild({
13725             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13726             cls: 'typeahead typeahead-long dropdown-menu shadow',
13727             style: 'display:none'
13728         });
13729         
13730         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13731         
13732     },
13733
13734     // private
13735     initTrigger : function(){
13736        
13737     },
13738
13739     // private
13740     onDestroy : function(){
13741         if(this.trigger){
13742             this.trigger.removeAllListeners();
13743           //  this.trigger.remove();
13744         }
13745         //if(this.wrap){
13746         //    this.wrap.remove();
13747         //}
13748         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13749     },
13750
13751     // private
13752     onFocus : function(){
13753         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13754         /*
13755         if(!this.mimicing){
13756             this.wrap.addClass('x-trigger-wrap-focus');
13757             this.mimicing = true;
13758             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13759             if(this.monitorTab){
13760                 this.el.on("keydown", this.checkTab, this);
13761             }
13762         }
13763         */
13764     },
13765
13766     // private
13767     checkTab : function(e){
13768         if(e.getKey() == e.TAB){
13769             this.triggerBlur();
13770         }
13771     },
13772
13773     // private
13774     onBlur : function(){
13775         // do nothing
13776     },
13777
13778     // private
13779     mimicBlur : function(e, t){
13780         /*
13781         if(!this.wrap.contains(t) && this.validateBlur()){
13782             this.triggerBlur();
13783         }
13784         */
13785     },
13786
13787     // private
13788     triggerBlur : function(){
13789         this.mimicing = false;
13790         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13791         if(this.monitorTab){
13792             this.el.un("keydown", this.checkTab, this);
13793         }
13794         //this.wrap.removeClass('x-trigger-wrap-focus');
13795         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13796     },
13797
13798     // private
13799     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13800     validateBlur : function(e, t){
13801         return true;
13802     },
13803
13804     // private
13805     onDisable : function(){
13806         this.inputEl().dom.disabled = true;
13807         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13808         //if(this.wrap){
13809         //    this.wrap.addClass('x-item-disabled');
13810         //}
13811     },
13812
13813     // private
13814     onEnable : function(){
13815         this.inputEl().dom.disabled = false;
13816         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13817         //if(this.wrap){
13818         //    this.el.removeClass('x-item-disabled');
13819         //}
13820     },
13821
13822     // private
13823     onShow : function(){
13824         var ae = this.getActionEl();
13825         
13826         if(ae){
13827             ae.dom.style.display = '';
13828             ae.dom.style.visibility = 'visible';
13829         }
13830     },
13831
13832     // private
13833     
13834     onHide : function(){
13835         var ae = this.getActionEl();
13836         ae.dom.style.display = 'none';
13837     },
13838
13839     /**
13840      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13841      * by an implementing function.
13842      * @method
13843      * @param {EventObject} e
13844      */
13845     onTriggerClick : Roo.emptyFn
13846 });
13847  
13848 /*
13849 * Licence: LGPL
13850 */
13851
13852 /**
13853  * @class Roo.bootstrap.CardUploader
13854  * @extends Roo.bootstrap.Button
13855  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13856  * @cfg {Number} errorTimeout default 3000
13857  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13858  * @cfg {Array}  html The button text.
13859
13860  *
13861  * @constructor
13862  * Create a new CardUploader
13863  * @param {Object} config The config object
13864  */
13865
13866 Roo.bootstrap.CardUploader = function(config){
13867     
13868  
13869     
13870     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13871     
13872     
13873     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13874         return r.data.id
13875      });
13876     
13877      this.addEvents({
13878          // raw events
13879         /**
13880          * @event preview
13881          * When a image is clicked on - and needs to display a slideshow or similar..
13882          * @param {Roo.bootstrap.Card} this
13883          * @param {Object} The image information data 
13884          *
13885          */
13886         'preview' : true,
13887          /**
13888          * @event download
13889          * When a the download link is clicked
13890          * @param {Roo.bootstrap.Card} this
13891          * @param {Object} The image information data  contains 
13892          */
13893         'download' : true
13894         
13895     });
13896 };
13897  
13898 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13899     
13900      
13901     errorTimeout : 3000,
13902      
13903     images : false,
13904    
13905     fileCollection : false,
13906     allowBlank : true,
13907     
13908     getAutoCreate : function()
13909     {
13910         
13911         var cfg =  {
13912             cls :'form-group' ,
13913             cn : [
13914                
13915                 {
13916                     tag: 'label',
13917                    //cls : 'input-group-addon',
13918                     html : this.fieldLabel
13919
13920                 },
13921
13922                 {
13923                     tag: 'input',
13924                     type : 'hidden',
13925                     name : this.name,
13926                     value : this.value,
13927                     cls : 'd-none  form-control'
13928                 },
13929                 
13930                 {
13931                     tag: 'input',
13932                     multiple : 'multiple',
13933                     type : 'file',
13934                     cls : 'd-none  roo-card-upload-selector'
13935                 },
13936                 
13937                 {
13938                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13939                 },
13940                 {
13941                     cls : 'card-columns roo-card-uploader-container'
13942                 }
13943
13944             ]
13945         };
13946            
13947          
13948         return cfg;
13949     },
13950     
13951     getChildContainer : function() /// what children are added to.
13952     {
13953         return this.containerEl;
13954     },
13955    
13956     getButtonContainer : function() /// what children are added to.
13957     {
13958         return this.el.select(".roo-card-uploader-button-container").first();
13959     },
13960    
13961     initEvents : function()
13962     {
13963         
13964         Roo.bootstrap.Input.prototype.initEvents.call(this);
13965         
13966         var t = this;
13967         this.addxtype({
13968             xns: Roo.bootstrap,
13969
13970             xtype : 'Button',
13971             container_method : 'getButtonContainer' ,            
13972             html :  this.html, // fix changable?
13973             cls : 'w-100 ',
13974             listeners : {
13975                 'click' : function(btn, e) {
13976                     t.onClick(e);
13977                 }
13978             }
13979         });
13980         
13981         
13982         
13983         
13984         this.urlAPI = (window.createObjectURL && window) || 
13985                                 (window.URL && URL.revokeObjectURL && URL) || 
13986                                 (window.webkitURL && webkitURL);
13987                         
13988          
13989          
13990          
13991         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13992         
13993         this.selectorEl.on('change', this.onFileSelected, this);
13994         if (this.images) {
13995             var t = this;
13996             this.images.forEach(function(img) {
13997                 t.addCard(img)
13998             });
13999             this.images = false;
14000         }
14001         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14002          
14003        
14004     },
14005     
14006    
14007     onClick : function(e)
14008     {
14009         e.preventDefault();
14010          
14011         this.selectorEl.dom.click();
14012          
14013     },
14014     
14015     onFileSelected : function(e)
14016     {
14017         e.preventDefault();
14018         
14019         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14020             return;
14021         }
14022         
14023         Roo.each(this.selectorEl.dom.files, function(file){    
14024             this.addFile(file);
14025         }, this);
14026          
14027     },
14028     
14029       
14030     
14031       
14032     
14033     addFile : function(file)
14034     {
14035            
14036         if(typeof(file) === 'string'){
14037             throw "Add file by name?"; // should not happen
14038             return;
14039         }
14040         
14041         if(!file || !this.urlAPI){
14042             return;
14043         }
14044         
14045         // file;
14046         // file.type;
14047         
14048         var _this = this;
14049         
14050         
14051         var url = _this.urlAPI.createObjectURL( file);
14052            
14053         this.addCard({
14054             id : Roo.bootstrap.CardUploader.ID--,
14055             is_uploaded : false,
14056             src : url,
14057             srcfile : file,
14058             title : file.name,
14059             mimetype : file.type,
14060             preview : false,
14061             is_deleted : 0
14062         });
14063         
14064     },
14065     
14066     /**
14067      * addCard - add an Attachment to the uploader
14068      * @param data - the data about the image to upload
14069      *
14070      * {
14071           id : 123
14072           title : "Title of file",
14073           is_uploaded : false,
14074           src : "http://.....",
14075           srcfile : { the File upload object },
14076           mimetype : file.type,
14077           preview : false,
14078           is_deleted : 0
14079           .. any other data...
14080         }
14081      *
14082      * 
14083     */
14084     
14085     addCard : function (data)
14086     {
14087         // hidden input element?
14088         // if the file is not an image...
14089         //then we need to use something other that and header_image
14090         var t = this;
14091         //   remove.....
14092         var footer = [
14093             {
14094                 xns : Roo.bootstrap,
14095                 xtype : 'CardFooter',
14096                  items: [
14097                     {
14098                         xns : Roo.bootstrap,
14099                         xtype : 'Element',
14100                         cls : 'd-flex',
14101                         items : [
14102                             
14103                             {
14104                                 xns : Roo.bootstrap,
14105                                 xtype : 'Button',
14106                                 html : String.format("<small>{0}</small>", data.title),
14107                                 cls : 'col-10 text-left',
14108                                 size: 'sm',
14109                                 weight: 'link',
14110                                 fa : 'download',
14111                                 listeners : {
14112                                     click : function() {
14113                                      
14114                                         t.fireEvent( "download", t, data );
14115                                     }
14116                                 }
14117                             },
14118                           
14119                             {
14120                                 xns : Roo.bootstrap,
14121                                 xtype : 'Button',
14122                                 style: 'max-height: 28px; ',
14123                                 size : 'sm',
14124                                 weight: 'danger',
14125                                 cls : 'col-2',
14126                                 fa : 'times',
14127                                 listeners : {
14128                                     click : function() {
14129                                         t.removeCard(data.id)
14130                                     }
14131                                 }
14132                             }
14133                         ]
14134                     }
14135                     
14136                 ] 
14137             }
14138             
14139         ];
14140         
14141         var cn = this.addxtype(
14142             {
14143                  
14144                 xns : Roo.bootstrap,
14145                 xtype : 'Card',
14146                 closeable : true,
14147                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14148                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14149                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14150                 data : data,
14151                 html : false,
14152                  
14153                 items : footer,
14154                 initEvents : function() {
14155                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14156                     var card = this;
14157                     this.imgEl = this.el.select('.card-img-top').first();
14158                     if (this.imgEl) {
14159                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14160                         this.imgEl.set({ 'pointer' : 'cursor' });
14161                                   
14162                     }
14163                     this.getCardFooter().addClass('p-1');
14164                     
14165                   
14166                 }
14167                 
14168             }
14169         );
14170         // dont' really need ot update items.
14171         // this.items.push(cn);
14172         this.fileCollection.add(cn);
14173         
14174         if (!data.srcfile) {
14175             this.updateInput();
14176             return;
14177         }
14178             
14179         var _t = this;
14180         var reader = new FileReader();
14181         reader.addEventListener("load", function() {  
14182             data.srcdata =  reader.result;
14183             _t.updateInput();
14184         });
14185         reader.readAsDataURL(data.srcfile);
14186         
14187         
14188         
14189     },
14190     removeCard : function(id)
14191     {
14192         
14193         var card  = this.fileCollection.get(id);
14194         card.data.is_deleted = 1;
14195         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14196         //this.fileCollection.remove(card);
14197         //this.items = this.items.filter(function(e) { return e != card });
14198         // dont' really need ot update items.
14199         card.el.dom.parentNode.removeChild(card.el.dom);
14200         this.updateInput();
14201
14202         
14203     },
14204     reset: function()
14205     {
14206         this.fileCollection.each(function(card) {
14207             if (card.el.dom && card.el.dom.parentNode) {
14208                 card.el.dom.parentNode.removeChild(card.el.dom);
14209             }
14210         });
14211         this.fileCollection.clear();
14212         this.updateInput();
14213     },
14214     
14215     updateInput : function()
14216     {
14217          var data = [];
14218         this.fileCollection.each(function(e) {
14219             data.push(e.data);
14220             
14221         });
14222         this.inputEl().dom.value = JSON.stringify(data);
14223         
14224         
14225         
14226     }
14227     
14228     
14229 });
14230
14231
14232 Roo.bootstrap.CardUploader.ID = -1;/*
14233  * Based on:
14234  * Ext JS Library 1.1.1
14235  * Copyright(c) 2006-2007, Ext JS, LLC.
14236  *
14237  * Originally Released Under LGPL - original licence link has changed is not relivant.
14238  *
14239  * Fork - LGPL
14240  * <script type="text/javascript">
14241  */
14242
14243
14244 /**
14245  * @class Roo.data.SortTypes
14246  * @singleton
14247  * Defines the default sorting (casting?) comparison functions used when sorting data.
14248  */
14249 Roo.data.SortTypes = {
14250     /**
14251      * Default sort that does nothing
14252      * @param {Mixed} s The value being converted
14253      * @return {Mixed} The comparison value
14254      */
14255     none : function(s){
14256         return s;
14257     },
14258     
14259     /**
14260      * The regular expression used to strip tags
14261      * @type {RegExp}
14262      * @property
14263      */
14264     stripTagsRE : /<\/?[^>]+>/gi,
14265     
14266     /**
14267      * Strips all HTML tags to sort on text only
14268      * @param {Mixed} s The value being converted
14269      * @return {String} The comparison value
14270      */
14271     asText : function(s){
14272         return String(s).replace(this.stripTagsRE, "");
14273     },
14274     
14275     /**
14276      * Strips all HTML tags to sort on text only - Case insensitive
14277      * @param {Mixed} s The value being converted
14278      * @return {String} The comparison value
14279      */
14280     asUCText : function(s){
14281         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14282     },
14283     
14284     /**
14285      * Case insensitive string
14286      * @param {Mixed} s The value being converted
14287      * @return {String} The comparison value
14288      */
14289     asUCString : function(s) {
14290         return String(s).toUpperCase();
14291     },
14292     
14293     /**
14294      * Date sorting
14295      * @param {Mixed} s The value being converted
14296      * @return {Number} The comparison value
14297      */
14298     asDate : function(s) {
14299         if(!s){
14300             return 0;
14301         }
14302         if(s instanceof Date){
14303             return s.getTime();
14304         }
14305         return Date.parse(String(s));
14306     },
14307     
14308     /**
14309      * Float sorting
14310      * @param {Mixed} s The value being converted
14311      * @return {Float} The comparison value
14312      */
14313     asFloat : function(s) {
14314         var val = parseFloat(String(s).replace(/,/g, ""));
14315         if(isNaN(val)) {
14316             val = 0;
14317         }
14318         return val;
14319     },
14320     
14321     /**
14322      * Integer sorting
14323      * @param {Mixed} s The value being converted
14324      * @return {Number} The comparison value
14325      */
14326     asInt : function(s) {
14327         var val = parseInt(String(s).replace(/,/g, ""));
14328         if(isNaN(val)) {
14329             val = 0;
14330         }
14331         return val;
14332     }
14333 };/*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343
14344 /**
14345 * @class Roo.data.Record
14346  * Instances of this class encapsulate both record <em>definition</em> information, and record
14347  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14348  * to access Records cached in an {@link Roo.data.Store} object.<br>
14349  * <p>
14350  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14351  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14352  * objects.<br>
14353  * <p>
14354  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14355  * @constructor
14356  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14357  * {@link #create}. The parameters are the same.
14358  * @param {Array} data An associative Array of data values keyed by the field name.
14359  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14360  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14361  * not specified an integer id is generated.
14362  */
14363 Roo.data.Record = function(data, id){
14364     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14365     this.data = data;
14366 };
14367
14368 /**
14369  * Generate a constructor for a specific record layout.
14370  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14371  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14372  * Each field definition object may contain the following properties: <ul>
14373  * <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,
14374  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14375  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14376  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14377  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14378  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14379  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14380  * this may be omitted.</p></li>
14381  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14382  * <ul><li>auto (Default, implies no conversion)</li>
14383  * <li>string</li>
14384  * <li>int</li>
14385  * <li>float</li>
14386  * <li>boolean</li>
14387  * <li>date</li></ul></p></li>
14388  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14389  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14390  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14391  * by the Reader into an object that will be stored in the Record. It is passed the
14392  * following parameters:<ul>
14393  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14394  * </ul></p></li>
14395  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14396  * </ul>
14397  * <br>usage:<br><pre><code>
14398 var TopicRecord = Roo.data.Record.create(
14399     {name: 'title', mapping: 'topic_title'},
14400     {name: 'author', mapping: 'username'},
14401     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14402     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14403     {name: 'lastPoster', mapping: 'user2'},
14404     {name: 'excerpt', mapping: 'post_text'}
14405 );
14406
14407 var myNewRecord = new TopicRecord({
14408     title: 'Do my job please',
14409     author: 'noobie',
14410     totalPosts: 1,
14411     lastPost: new Date(),
14412     lastPoster: 'Animal',
14413     excerpt: 'No way dude!'
14414 });
14415 myStore.add(myNewRecord);
14416 </code></pre>
14417  * @method create
14418  * @static
14419  */
14420 Roo.data.Record.create = function(o){
14421     var f = function(){
14422         f.superclass.constructor.apply(this, arguments);
14423     };
14424     Roo.extend(f, Roo.data.Record);
14425     var p = f.prototype;
14426     p.fields = new Roo.util.MixedCollection(false, function(field){
14427         return field.name;
14428     });
14429     for(var i = 0, len = o.length; i < len; i++){
14430         p.fields.add(new Roo.data.Field(o[i]));
14431     }
14432     f.getField = function(name){
14433         return p.fields.get(name);  
14434     };
14435     return f;
14436 };
14437
14438 Roo.data.Record.AUTO_ID = 1000;
14439 Roo.data.Record.EDIT = 'edit';
14440 Roo.data.Record.REJECT = 'reject';
14441 Roo.data.Record.COMMIT = 'commit';
14442
14443 Roo.data.Record.prototype = {
14444     /**
14445      * Readonly flag - true if this record has been modified.
14446      * @type Boolean
14447      */
14448     dirty : false,
14449     editing : false,
14450     error: null,
14451     modified: null,
14452
14453     // private
14454     join : function(store){
14455         this.store = store;
14456     },
14457
14458     /**
14459      * Set the named field to the specified value.
14460      * @param {String} name The name of the field to set.
14461      * @param {Object} value The value to set the field to.
14462      */
14463     set : function(name, value){
14464         if(this.data[name] == value){
14465             return;
14466         }
14467         this.dirty = true;
14468         if(!this.modified){
14469             this.modified = {};
14470         }
14471         if(typeof this.modified[name] == 'undefined'){
14472             this.modified[name] = this.data[name];
14473         }
14474         this.data[name] = value;
14475         if(!this.editing && this.store){
14476             this.store.afterEdit(this);
14477         }       
14478     },
14479
14480     /**
14481      * Get the value of the named field.
14482      * @param {String} name The name of the field to get the value of.
14483      * @return {Object} The value of the field.
14484      */
14485     get : function(name){
14486         return this.data[name]; 
14487     },
14488
14489     // private
14490     beginEdit : function(){
14491         this.editing = true;
14492         this.modified = {}; 
14493     },
14494
14495     // private
14496     cancelEdit : function(){
14497         this.editing = false;
14498         delete this.modified;
14499     },
14500
14501     // private
14502     endEdit : function(){
14503         this.editing = false;
14504         if(this.dirty && this.store){
14505             this.store.afterEdit(this);
14506         }
14507     },
14508
14509     /**
14510      * Usually called by the {@link Roo.data.Store} which owns the Record.
14511      * Rejects all changes made to the Record since either creation, or the last commit operation.
14512      * Modified fields are reverted to their original values.
14513      * <p>
14514      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14515      * of reject operations.
14516      */
14517     reject : function(){
14518         var m = this.modified;
14519         for(var n in m){
14520             if(typeof m[n] != "function"){
14521                 this.data[n] = m[n];
14522             }
14523         }
14524         this.dirty = false;
14525         delete this.modified;
14526         this.editing = false;
14527         if(this.store){
14528             this.store.afterReject(this);
14529         }
14530     },
14531
14532     /**
14533      * Usually called by the {@link Roo.data.Store} which owns the Record.
14534      * Commits all changes made to the Record since either creation, or the last commit operation.
14535      * <p>
14536      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14537      * of commit operations.
14538      */
14539     commit : function(){
14540         this.dirty = false;
14541         delete this.modified;
14542         this.editing = false;
14543         if(this.store){
14544             this.store.afterCommit(this);
14545         }
14546     },
14547
14548     // private
14549     hasError : function(){
14550         return this.error != null;
14551     },
14552
14553     // private
14554     clearError : function(){
14555         this.error = null;
14556     },
14557
14558     /**
14559      * Creates a copy of this record.
14560      * @param {String} id (optional) A new record id if you don't want to use this record's id
14561      * @return {Record}
14562      */
14563     copy : function(newId) {
14564         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14565     }
14566 };/*
14567  * Based on:
14568  * Ext JS Library 1.1.1
14569  * Copyright(c) 2006-2007, Ext JS, LLC.
14570  *
14571  * Originally Released Under LGPL - original licence link has changed is not relivant.
14572  *
14573  * Fork - LGPL
14574  * <script type="text/javascript">
14575  */
14576
14577
14578
14579 /**
14580  * @class Roo.data.Store
14581  * @extends Roo.util.Observable
14582  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14583  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14584  * <p>
14585  * 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
14586  * has no knowledge of the format of the data returned by the Proxy.<br>
14587  * <p>
14588  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14589  * instances from the data object. These records are cached and made available through accessor functions.
14590  * @constructor
14591  * Creates a new Store.
14592  * @param {Object} config A config object containing the objects needed for the Store to access data,
14593  * and read the data into Records.
14594  */
14595 Roo.data.Store = function(config){
14596     this.data = new Roo.util.MixedCollection(false);
14597     this.data.getKey = function(o){
14598         return o.id;
14599     };
14600     this.baseParams = {};
14601     // private
14602     this.paramNames = {
14603         "start" : "start",
14604         "limit" : "limit",
14605         "sort" : "sort",
14606         "dir" : "dir",
14607         "multisort" : "_multisort"
14608     };
14609
14610     if(config && config.data){
14611         this.inlineData = config.data;
14612         delete config.data;
14613     }
14614
14615     Roo.apply(this, config);
14616     
14617     if(this.reader){ // reader passed
14618         this.reader = Roo.factory(this.reader, Roo.data);
14619         this.reader.xmodule = this.xmodule || false;
14620         if(!this.recordType){
14621             this.recordType = this.reader.recordType;
14622         }
14623         if(this.reader.onMetaChange){
14624             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14625         }
14626     }
14627
14628     if(this.recordType){
14629         this.fields = this.recordType.prototype.fields;
14630     }
14631     this.modified = [];
14632
14633     this.addEvents({
14634         /**
14635          * @event datachanged
14636          * Fires when the data cache has changed, and a widget which is using this Store
14637          * as a Record cache should refresh its view.
14638          * @param {Store} this
14639          */
14640         datachanged : true,
14641         /**
14642          * @event metachange
14643          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14644          * @param {Store} this
14645          * @param {Object} meta The JSON metadata
14646          */
14647         metachange : true,
14648         /**
14649          * @event add
14650          * Fires when Records have been added to the Store
14651          * @param {Store} this
14652          * @param {Roo.data.Record[]} records The array of Records added
14653          * @param {Number} index The index at which the record(s) were added
14654          */
14655         add : true,
14656         /**
14657          * @event remove
14658          * Fires when a Record has been removed from the Store
14659          * @param {Store} this
14660          * @param {Roo.data.Record} record The Record that was removed
14661          * @param {Number} index The index at which the record was removed
14662          */
14663         remove : true,
14664         /**
14665          * @event update
14666          * Fires when a Record has been updated
14667          * @param {Store} this
14668          * @param {Roo.data.Record} record The Record that was updated
14669          * @param {String} operation The update operation being performed.  Value may be one of:
14670          * <pre><code>
14671  Roo.data.Record.EDIT
14672  Roo.data.Record.REJECT
14673  Roo.data.Record.COMMIT
14674          * </code></pre>
14675          */
14676         update : true,
14677         /**
14678          * @event clear
14679          * Fires when the data cache has been cleared.
14680          * @param {Store} this
14681          */
14682         clear : true,
14683         /**
14684          * @event beforeload
14685          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14686          * the load action will be canceled.
14687          * @param {Store} this
14688          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14689          */
14690         beforeload : true,
14691         /**
14692          * @event beforeloadadd
14693          * Fires after a new set of Records has been loaded.
14694          * @param {Store} this
14695          * @param {Roo.data.Record[]} records The Records that were loaded
14696          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14697          */
14698         beforeloadadd : true,
14699         /**
14700          * @event load
14701          * Fires after a new set of Records has been loaded, before they are added to the store.
14702          * @param {Store} this
14703          * @param {Roo.data.Record[]} records The Records that were loaded
14704          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14705          * @params {Object} return from reader
14706          */
14707         load : true,
14708         /**
14709          * @event loadexception
14710          * Fires if an exception occurs in the Proxy during loading.
14711          * Called with the signature of the Proxy's "loadexception" event.
14712          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14713          * 
14714          * @param {Proxy} 
14715          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14716          * @param {Object} load options 
14717          * @param {Object} jsonData from your request (normally this contains the Exception)
14718          */
14719         loadexception : true
14720     });
14721     
14722     if(this.proxy){
14723         this.proxy = Roo.factory(this.proxy, Roo.data);
14724         this.proxy.xmodule = this.xmodule || false;
14725         this.relayEvents(this.proxy,  ["loadexception"]);
14726     }
14727     this.sortToggle = {};
14728     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14729
14730     Roo.data.Store.superclass.constructor.call(this);
14731
14732     if(this.inlineData){
14733         this.loadData(this.inlineData);
14734         delete this.inlineData;
14735     }
14736 };
14737
14738 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14739      /**
14740     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14741     * without a remote query - used by combo/forms at present.
14742     */
14743     
14744     /**
14745     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14746     */
14747     /**
14748     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14749     */
14750     /**
14751     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14752     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14753     */
14754     /**
14755     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14756     * on any HTTP request
14757     */
14758     /**
14759     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14760     */
14761     /**
14762     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14763     */
14764     multiSort: false,
14765     /**
14766     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14767     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14768     */
14769     remoteSort : false,
14770
14771     /**
14772     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14773      * loaded or when a record is removed. (defaults to false).
14774     */
14775     pruneModifiedRecords : false,
14776
14777     // private
14778     lastOptions : null,
14779
14780     /**
14781      * Add Records to the Store and fires the add event.
14782      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14783      */
14784     add : function(records){
14785         records = [].concat(records);
14786         for(var i = 0, len = records.length; i < len; i++){
14787             records[i].join(this);
14788         }
14789         var index = this.data.length;
14790         this.data.addAll(records);
14791         this.fireEvent("add", this, records, index);
14792     },
14793
14794     /**
14795      * Remove a Record from the Store and fires the remove event.
14796      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14797      */
14798     remove : function(record){
14799         var index = this.data.indexOf(record);
14800         this.data.removeAt(index);
14801  
14802         if(this.pruneModifiedRecords){
14803             this.modified.remove(record);
14804         }
14805         this.fireEvent("remove", this, record, index);
14806     },
14807
14808     /**
14809      * Remove all Records from the Store and fires the clear event.
14810      */
14811     removeAll : function(){
14812         this.data.clear();
14813         if(this.pruneModifiedRecords){
14814             this.modified = [];
14815         }
14816         this.fireEvent("clear", this);
14817     },
14818
14819     /**
14820      * Inserts Records to the Store at the given index and fires the add event.
14821      * @param {Number} index The start index at which to insert the passed Records.
14822      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14823      */
14824     insert : function(index, records){
14825         records = [].concat(records);
14826         for(var i = 0, len = records.length; i < len; i++){
14827             this.data.insert(index, records[i]);
14828             records[i].join(this);
14829         }
14830         this.fireEvent("add", this, records, index);
14831     },
14832
14833     /**
14834      * Get the index within the cache of the passed Record.
14835      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14836      * @return {Number} The index of the passed Record. Returns -1 if not found.
14837      */
14838     indexOf : function(record){
14839         return this.data.indexOf(record);
14840     },
14841
14842     /**
14843      * Get the index within the cache of the Record with the passed id.
14844      * @param {String} id The id of the Record to find.
14845      * @return {Number} The index of the Record. Returns -1 if not found.
14846      */
14847     indexOfId : function(id){
14848         return this.data.indexOfKey(id);
14849     },
14850
14851     /**
14852      * Get the Record with the specified id.
14853      * @param {String} id The id of the Record to find.
14854      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14855      */
14856     getById : function(id){
14857         return this.data.key(id);
14858     },
14859
14860     /**
14861      * Get the Record at the specified index.
14862      * @param {Number} index The index of the Record to find.
14863      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14864      */
14865     getAt : function(index){
14866         return this.data.itemAt(index);
14867     },
14868
14869     /**
14870      * Returns a range of Records between specified indices.
14871      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14872      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14873      * @return {Roo.data.Record[]} An array of Records
14874      */
14875     getRange : function(start, end){
14876         return this.data.getRange(start, end);
14877     },
14878
14879     // private
14880     storeOptions : function(o){
14881         o = Roo.apply({}, o);
14882         delete o.callback;
14883         delete o.scope;
14884         this.lastOptions = o;
14885     },
14886
14887     /**
14888      * Loads the Record cache from the configured Proxy using the configured Reader.
14889      * <p>
14890      * If using remote paging, then the first load call must specify the <em>start</em>
14891      * and <em>limit</em> properties in the options.params property to establish the initial
14892      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14893      * <p>
14894      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14895      * and this call will return before the new data has been loaded. Perform any post-processing
14896      * in a callback function, or in a "load" event handler.</strong>
14897      * <p>
14898      * @param {Object} options An object containing properties which control loading options:<ul>
14899      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14900      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14901      * passed the following arguments:<ul>
14902      * <li>r : Roo.data.Record[]</li>
14903      * <li>options: Options object from the load call</li>
14904      * <li>success: Boolean success indicator</li></ul></li>
14905      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14906      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14907      * </ul>
14908      */
14909     load : function(options){
14910         options = options || {};
14911         if(this.fireEvent("beforeload", this, options) !== false){
14912             this.storeOptions(options);
14913             var p = Roo.apply(options.params || {}, this.baseParams);
14914             // if meta was not loaded from remote source.. try requesting it.
14915             if (!this.reader.metaFromRemote) {
14916                 p._requestMeta = 1;
14917             }
14918             if(this.sortInfo && this.remoteSort){
14919                 var pn = this.paramNames;
14920                 p[pn["sort"]] = this.sortInfo.field;
14921                 p[pn["dir"]] = this.sortInfo.direction;
14922             }
14923             if (this.multiSort) {
14924                 var pn = this.paramNames;
14925                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14926             }
14927             
14928             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14929         }
14930     },
14931
14932     /**
14933      * Reloads the Record cache from the configured Proxy using the configured Reader and
14934      * the options from the last load operation performed.
14935      * @param {Object} options (optional) An object containing properties which may override the options
14936      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14937      * the most recently used options are reused).
14938      */
14939     reload : function(options){
14940         this.load(Roo.applyIf(options||{}, this.lastOptions));
14941     },
14942
14943     // private
14944     // Called as a callback by the Reader during a load operation.
14945     loadRecords : function(o, options, success){
14946         if(!o || success === false){
14947             if(success !== false){
14948                 this.fireEvent("load", this, [], options, o);
14949             }
14950             if(options.callback){
14951                 options.callback.call(options.scope || this, [], options, false);
14952             }
14953             return;
14954         }
14955         // if data returned failure - throw an exception.
14956         if (o.success === false) {
14957             // show a message if no listener is registered.
14958             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14959                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14960             }
14961             // loadmask wil be hooked into this..
14962             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14963             return;
14964         }
14965         var r = o.records, t = o.totalRecords || r.length;
14966         
14967         this.fireEvent("beforeloadadd", this, r, options, o);
14968         
14969         if(!options || options.add !== true){
14970             if(this.pruneModifiedRecords){
14971                 this.modified = [];
14972             }
14973             for(var i = 0, len = r.length; i < len; i++){
14974                 r[i].join(this);
14975             }
14976             if(this.snapshot){
14977                 this.data = this.snapshot;
14978                 delete this.snapshot;
14979             }
14980             this.data.clear();
14981             this.data.addAll(r);
14982             this.totalLength = t;
14983             this.applySort();
14984             this.fireEvent("datachanged", this);
14985         }else{
14986             this.totalLength = Math.max(t, this.data.length+r.length);
14987             this.add(r);
14988         }
14989         
14990         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14991                 
14992             var e = new Roo.data.Record({});
14993
14994             e.set(this.parent.displayField, this.parent.emptyTitle);
14995             e.set(this.parent.valueField, '');
14996
14997             this.insert(0, e);
14998         }
14999             
15000         this.fireEvent("load", this, r, options, o);
15001         if(options.callback){
15002             options.callback.call(options.scope || this, r, options, true);
15003         }
15004     },
15005
15006
15007     /**
15008      * Loads data from a passed data block. A Reader which understands the format of the data
15009      * must have been configured in the constructor.
15010      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15011      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15012      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15013      */
15014     loadData : function(o, append){
15015         var r = this.reader.readRecords(o);
15016         this.loadRecords(r, {add: append}, true);
15017     },
15018     
15019      /**
15020      * using 'cn' the nested child reader read the child array into it's child stores.
15021      * @param {Object} rec The record with a 'children array
15022      */
15023     loadDataFromChildren : function(rec)
15024     {
15025         this.loadData(this.reader.toLoadData(rec));
15026     },
15027     
15028
15029     /**
15030      * Gets the number of cached records.
15031      * <p>
15032      * <em>If using paging, this may not be the total size of the dataset. If the data object
15033      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15034      * the data set size</em>
15035      */
15036     getCount : function(){
15037         return this.data.length || 0;
15038     },
15039
15040     /**
15041      * Gets the total number of records in the dataset as returned by the server.
15042      * <p>
15043      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15044      * the dataset size</em>
15045      */
15046     getTotalCount : function(){
15047         return this.totalLength || 0;
15048     },
15049
15050     /**
15051      * Returns the sort state of the Store as an object with two properties:
15052      * <pre><code>
15053  field {String} The name of the field by which the Records are sorted
15054  direction {String} The sort order, "ASC" or "DESC"
15055      * </code></pre>
15056      */
15057     getSortState : function(){
15058         return this.sortInfo;
15059     },
15060
15061     // private
15062     applySort : function(){
15063         if(this.sortInfo && !this.remoteSort){
15064             var s = this.sortInfo, f = s.field;
15065             var st = this.fields.get(f).sortType;
15066             var fn = function(r1, r2){
15067                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15068                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15069             };
15070             this.data.sort(s.direction, fn);
15071             if(this.snapshot && this.snapshot != this.data){
15072                 this.snapshot.sort(s.direction, fn);
15073             }
15074         }
15075     },
15076
15077     /**
15078      * Sets the default sort column and order to be used by the next load operation.
15079      * @param {String} fieldName The name of the field to sort by.
15080      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15081      */
15082     setDefaultSort : function(field, dir){
15083         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15084     },
15085
15086     /**
15087      * Sort the Records.
15088      * If remote sorting is used, the sort is performed on the server, and the cache is
15089      * reloaded. If local sorting is used, the cache is sorted internally.
15090      * @param {String} fieldName The name of the field to sort by.
15091      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15092      */
15093     sort : function(fieldName, dir){
15094         var f = this.fields.get(fieldName);
15095         if(!dir){
15096             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15097             
15098             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15099                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15100             }else{
15101                 dir = f.sortDir;
15102             }
15103         }
15104         this.sortToggle[f.name] = dir;
15105         this.sortInfo = {field: f.name, direction: dir};
15106         if(!this.remoteSort){
15107             this.applySort();
15108             this.fireEvent("datachanged", this);
15109         }else{
15110             this.load(this.lastOptions);
15111         }
15112     },
15113
15114     /**
15115      * Calls the specified function for each of the Records in the cache.
15116      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15117      * Returning <em>false</em> aborts and exits the iteration.
15118      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15119      */
15120     each : function(fn, scope){
15121         this.data.each(fn, scope);
15122     },
15123
15124     /**
15125      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15126      * (e.g., during paging).
15127      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15128      */
15129     getModifiedRecords : function(){
15130         return this.modified;
15131     },
15132
15133     // private
15134     createFilterFn : function(property, value, anyMatch){
15135         if(!value.exec){ // not a regex
15136             value = String(value);
15137             if(value.length == 0){
15138                 return false;
15139             }
15140             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15141         }
15142         return function(r){
15143             return value.test(r.data[property]);
15144         };
15145     },
15146
15147     /**
15148      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15149      * @param {String} property A field on your records
15150      * @param {Number} start The record index to start at (defaults to 0)
15151      * @param {Number} end The last record index to include (defaults to length - 1)
15152      * @return {Number} The sum
15153      */
15154     sum : function(property, start, end){
15155         var rs = this.data.items, v = 0;
15156         start = start || 0;
15157         end = (end || end === 0) ? end : rs.length-1;
15158
15159         for(var i = start; i <= end; i++){
15160             v += (rs[i].data[property] || 0);
15161         }
15162         return v;
15163     },
15164
15165     /**
15166      * Filter the records by a specified property.
15167      * @param {String} field A field on your records
15168      * @param {String/RegExp} value Either a string that the field
15169      * should start with or a RegExp to test against the field
15170      * @param {Boolean} anyMatch True to match any part not just the beginning
15171      */
15172     filter : function(property, value, anyMatch){
15173         var fn = this.createFilterFn(property, value, anyMatch);
15174         return fn ? this.filterBy(fn) : this.clearFilter();
15175     },
15176
15177     /**
15178      * Filter by a function. The specified function will be called with each
15179      * record in this data source. If the function returns true the record is included,
15180      * otherwise it is filtered.
15181      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15182      * @param {Object} scope (optional) The scope of the function (defaults to this)
15183      */
15184     filterBy : function(fn, scope){
15185         this.snapshot = this.snapshot || this.data;
15186         this.data = this.queryBy(fn, scope||this);
15187         this.fireEvent("datachanged", this);
15188     },
15189
15190     /**
15191      * Query the records by a specified property.
15192      * @param {String} field A field on your records
15193      * @param {String/RegExp} value Either a string that the field
15194      * should start with or a RegExp to test against the field
15195      * @param {Boolean} anyMatch True to match any part not just the beginning
15196      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15197      */
15198     query : function(property, value, anyMatch){
15199         var fn = this.createFilterFn(property, value, anyMatch);
15200         return fn ? this.queryBy(fn) : this.data.clone();
15201     },
15202
15203     /**
15204      * Query by a function. The specified function will be called with each
15205      * record in this data source. If the function returns true the record is included
15206      * in the results.
15207      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15208      * @param {Object} scope (optional) The scope of the function (defaults to this)
15209       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210      **/
15211     queryBy : function(fn, scope){
15212         var data = this.snapshot || this.data;
15213         return data.filterBy(fn, scope||this);
15214     },
15215
15216     /**
15217      * Collects unique values for a particular dataIndex from this store.
15218      * @param {String} dataIndex The property to collect
15219      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15220      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15221      * @return {Array} An array of the unique values
15222      **/
15223     collect : function(dataIndex, allowNull, bypassFilter){
15224         var d = (bypassFilter === true && this.snapshot) ?
15225                 this.snapshot.items : this.data.items;
15226         var v, sv, r = [], l = {};
15227         for(var i = 0, len = d.length; i < len; i++){
15228             v = d[i].data[dataIndex];
15229             sv = String(v);
15230             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15231                 l[sv] = true;
15232                 r[r.length] = v;
15233             }
15234         }
15235         return r;
15236     },
15237
15238     /**
15239      * Revert to a view of the Record cache with no filtering applied.
15240      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15241      */
15242     clearFilter : function(suppressEvent){
15243         if(this.snapshot && this.snapshot != this.data){
15244             this.data = this.snapshot;
15245             delete this.snapshot;
15246             if(suppressEvent !== true){
15247                 this.fireEvent("datachanged", this);
15248             }
15249         }
15250     },
15251
15252     // private
15253     afterEdit : function(record){
15254         if(this.modified.indexOf(record) == -1){
15255             this.modified.push(record);
15256         }
15257         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15258     },
15259     
15260     // private
15261     afterReject : function(record){
15262         this.modified.remove(record);
15263         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15264     },
15265
15266     // private
15267     afterCommit : function(record){
15268         this.modified.remove(record);
15269         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15270     },
15271
15272     /**
15273      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15274      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15275      */
15276     commitChanges : function(){
15277         var m = this.modified.slice(0);
15278         this.modified = [];
15279         for(var i = 0, len = m.length; i < len; i++){
15280             m[i].commit();
15281         }
15282     },
15283
15284     /**
15285      * Cancel outstanding changes on all changed records.
15286      */
15287     rejectChanges : function(){
15288         var m = this.modified.slice(0);
15289         this.modified = [];
15290         for(var i = 0, len = m.length; i < len; i++){
15291             m[i].reject();
15292         }
15293     },
15294
15295     onMetaChange : function(meta, rtype, o){
15296         this.recordType = rtype;
15297         this.fields = rtype.prototype.fields;
15298         delete this.snapshot;
15299         this.sortInfo = meta.sortInfo || this.sortInfo;
15300         this.modified = [];
15301         this.fireEvent('metachange', this, this.reader.meta);
15302     },
15303     
15304     moveIndex : function(data, type)
15305     {
15306         var index = this.indexOf(data);
15307         
15308         var newIndex = index + type;
15309         
15310         this.remove(data);
15311         
15312         this.insert(newIndex, data);
15313         
15314     }
15315 });/*
15316  * Based on:
15317  * Ext JS Library 1.1.1
15318  * Copyright(c) 2006-2007, Ext JS, LLC.
15319  *
15320  * Originally Released Under LGPL - original licence link has changed is not relivant.
15321  *
15322  * Fork - LGPL
15323  * <script type="text/javascript">
15324  */
15325
15326 /**
15327  * @class Roo.data.SimpleStore
15328  * @extends Roo.data.Store
15329  * Small helper class to make creating Stores from Array data easier.
15330  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15331  * @cfg {Array} fields An array of field definition objects, or field name strings.
15332  * @cfg {Object} an existing reader (eg. copied from another store)
15333  * @cfg {Array} data The multi-dimensional array of data
15334  * @constructor
15335  * @param {Object} config
15336  */
15337 Roo.data.SimpleStore = function(config)
15338 {
15339     Roo.data.SimpleStore.superclass.constructor.call(this, {
15340         isLocal : true,
15341         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15342                 id: config.id
15343             },
15344             Roo.data.Record.create(config.fields)
15345         ),
15346         proxy : new Roo.data.MemoryProxy(config.data)
15347     });
15348     this.load();
15349 };
15350 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15351  * Based on:
15352  * Ext JS Library 1.1.1
15353  * Copyright(c) 2006-2007, Ext JS, LLC.
15354  *
15355  * Originally Released Under LGPL - original licence link has changed is not relivant.
15356  *
15357  * Fork - LGPL
15358  * <script type="text/javascript">
15359  */
15360
15361 /**
15362 /**
15363  * @extends Roo.data.Store
15364  * @class Roo.data.JsonStore
15365  * Small helper class to make creating Stores for JSON data easier. <br/>
15366 <pre><code>
15367 var store = new Roo.data.JsonStore({
15368     url: 'get-images.php',
15369     root: 'images',
15370     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15371 });
15372 </code></pre>
15373  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15374  * JsonReader and HttpProxy (unless inline data is provided).</b>
15375  * @cfg {Array} fields An array of field definition objects, or field name strings.
15376  * @constructor
15377  * @param {Object} config
15378  */
15379 Roo.data.JsonStore = function(c){
15380     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15381         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15382         reader: new Roo.data.JsonReader(c, c.fields)
15383     }));
15384 };
15385 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15386  * Based on:
15387  * Ext JS Library 1.1.1
15388  * Copyright(c) 2006-2007, Ext JS, LLC.
15389  *
15390  * Originally Released Under LGPL - original licence link has changed is not relivant.
15391  *
15392  * Fork - LGPL
15393  * <script type="text/javascript">
15394  */
15395
15396  
15397 Roo.data.Field = function(config){
15398     if(typeof config == "string"){
15399         config = {name: config};
15400     }
15401     Roo.apply(this, config);
15402     
15403     if(!this.type){
15404         this.type = "auto";
15405     }
15406     
15407     var st = Roo.data.SortTypes;
15408     // named sortTypes are supported, here we look them up
15409     if(typeof this.sortType == "string"){
15410         this.sortType = st[this.sortType];
15411     }
15412     
15413     // set default sortType for strings and dates
15414     if(!this.sortType){
15415         switch(this.type){
15416             case "string":
15417                 this.sortType = st.asUCString;
15418                 break;
15419             case "date":
15420                 this.sortType = st.asDate;
15421                 break;
15422             default:
15423                 this.sortType = st.none;
15424         }
15425     }
15426
15427     // define once
15428     var stripRe = /[\$,%]/g;
15429
15430     // prebuilt conversion function for this field, instead of
15431     // switching every time we're reading a value
15432     if(!this.convert){
15433         var cv, dateFormat = this.dateFormat;
15434         switch(this.type){
15435             case "":
15436             case "auto":
15437             case undefined:
15438                 cv = function(v){ return v; };
15439                 break;
15440             case "string":
15441                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15442                 break;
15443             case "int":
15444                 cv = function(v){
15445                     return v !== undefined && v !== null && v !== '' ?
15446                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15447                     };
15448                 break;
15449             case "float":
15450                 cv = function(v){
15451                     return v !== undefined && v !== null && v !== '' ?
15452                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15453                     };
15454                 break;
15455             case "bool":
15456             case "boolean":
15457                 cv = function(v){ return v === true || v === "true" || v == 1; };
15458                 break;
15459             case "date":
15460                 cv = function(v){
15461                     if(!v){
15462                         return '';
15463                     }
15464                     if(v instanceof Date){
15465                         return v;
15466                     }
15467                     if(dateFormat){
15468                         if(dateFormat == "timestamp"){
15469                             return new Date(v*1000);
15470                         }
15471                         return Date.parseDate(v, dateFormat);
15472                     }
15473                     var parsed = Date.parse(v);
15474                     return parsed ? new Date(parsed) : null;
15475                 };
15476              break;
15477             
15478         }
15479         this.convert = cv;
15480     }
15481 };
15482
15483 Roo.data.Field.prototype = {
15484     dateFormat: null,
15485     defaultValue: "",
15486     mapping: null,
15487     sortType : null,
15488     sortDir : "ASC"
15489 };/*
15490  * Based on:
15491  * Ext JS Library 1.1.1
15492  * Copyright(c) 2006-2007, Ext JS, LLC.
15493  *
15494  * Originally Released Under LGPL - original licence link has changed is not relivant.
15495  *
15496  * Fork - LGPL
15497  * <script type="text/javascript">
15498  */
15499  
15500 // Base class for reading structured data from a data source.  This class is intended to be
15501 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15502
15503 /**
15504  * @class Roo.data.DataReader
15505  * Base class for reading structured data from a data source.  This class is intended to be
15506  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15507  */
15508
15509 Roo.data.DataReader = function(meta, recordType){
15510     
15511     this.meta = meta;
15512     
15513     this.recordType = recordType instanceof Array ? 
15514         Roo.data.Record.create(recordType) : recordType;
15515 };
15516
15517 Roo.data.DataReader.prototype = {
15518     
15519     
15520     readerType : 'Data',
15521      /**
15522      * Create an empty record
15523      * @param {Object} data (optional) - overlay some values
15524      * @return {Roo.data.Record} record created.
15525      */
15526     newRow :  function(d) {
15527         var da =  {};
15528         this.recordType.prototype.fields.each(function(c) {
15529             switch( c.type) {
15530                 case 'int' : da[c.name] = 0; break;
15531                 case 'date' : da[c.name] = new Date(); break;
15532                 case 'float' : da[c.name] = 0.0; break;
15533                 case 'boolean' : da[c.name] = false; break;
15534                 default : da[c.name] = ""; break;
15535             }
15536             
15537         });
15538         return new this.recordType(Roo.apply(da, d));
15539     }
15540     
15541     
15542 };/*
15543  * Based on:
15544  * Ext JS Library 1.1.1
15545  * Copyright(c) 2006-2007, Ext JS, LLC.
15546  *
15547  * Originally Released Under LGPL - original licence link has changed is not relivant.
15548  *
15549  * Fork - LGPL
15550  * <script type="text/javascript">
15551  */
15552
15553 /**
15554  * @class Roo.data.DataProxy
15555  * @extends Roo.data.Observable
15556  * This class is an abstract base class for implementations which provide retrieval of
15557  * unformatted data objects.<br>
15558  * <p>
15559  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15560  * (of the appropriate type which knows how to parse the data object) to provide a block of
15561  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15562  * <p>
15563  * Custom implementations must implement the load method as described in
15564  * {@link Roo.data.HttpProxy#load}.
15565  */
15566 Roo.data.DataProxy = function(){
15567     this.addEvents({
15568         /**
15569          * @event beforeload
15570          * Fires before a network request is made to retrieve a data object.
15571          * @param {Object} This DataProxy object.
15572          * @param {Object} params The params parameter to the load function.
15573          */
15574         beforeload : true,
15575         /**
15576          * @event load
15577          * Fires before the load method's callback is called.
15578          * @param {Object} This DataProxy object.
15579          * @param {Object} o The data object.
15580          * @param {Object} arg The callback argument object passed to the load function.
15581          */
15582         load : true,
15583         /**
15584          * @event loadexception
15585          * Fires if an Exception occurs during data retrieval.
15586          * @param {Object} This DataProxy object.
15587          * @param {Object} o The data object.
15588          * @param {Object} arg The callback argument object passed to the load function.
15589          * @param {Object} e The Exception.
15590          */
15591         loadexception : true
15592     });
15593     Roo.data.DataProxy.superclass.constructor.call(this);
15594 };
15595
15596 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15597
15598     /**
15599      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15600      */
15601 /*
15602  * Based on:
15603  * Ext JS Library 1.1.1
15604  * Copyright(c) 2006-2007, Ext JS, LLC.
15605  *
15606  * Originally Released Under LGPL - original licence link has changed is not relivant.
15607  *
15608  * Fork - LGPL
15609  * <script type="text/javascript">
15610  */
15611 /**
15612  * @class Roo.data.MemoryProxy
15613  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15614  * to the Reader when its load method is called.
15615  * @constructor
15616  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15617  */
15618 Roo.data.MemoryProxy = function(data){
15619     if (data.data) {
15620         data = data.data;
15621     }
15622     Roo.data.MemoryProxy.superclass.constructor.call(this);
15623     this.data = data;
15624 };
15625
15626 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15627     
15628     /**
15629      * Load data from the requested source (in this case an in-memory
15630      * data object passed to the constructor), read the data object into
15631      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15632      * process that block using the passed callback.
15633      * @param {Object} params This parameter is not used by the MemoryProxy class.
15634      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15635      * object into a block of Roo.data.Records.
15636      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15637      * The function must be passed <ul>
15638      * <li>The Record block object</li>
15639      * <li>The "arg" argument from the load function</li>
15640      * <li>A boolean success indicator</li>
15641      * </ul>
15642      * @param {Object} scope The scope in which to call the callback
15643      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15644      */
15645     load : function(params, reader, callback, scope, arg){
15646         params = params || {};
15647         var result;
15648         try {
15649             result = reader.readRecords(params.data ? params.data :this.data);
15650         }catch(e){
15651             this.fireEvent("loadexception", this, arg, null, e);
15652             callback.call(scope, null, arg, false);
15653             return;
15654         }
15655         callback.call(scope, result, arg, true);
15656     },
15657     
15658     // private
15659     update : function(params, records){
15660         
15661     }
15662 });/*
15663  * Based on:
15664  * Ext JS Library 1.1.1
15665  * Copyright(c) 2006-2007, Ext JS, LLC.
15666  *
15667  * Originally Released Under LGPL - original licence link has changed is not relivant.
15668  *
15669  * Fork - LGPL
15670  * <script type="text/javascript">
15671  */
15672 /**
15673  * @class Roo.data.HttpProxy
15674  * @extends Roo.data.DataProxy
15675  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15676  * configured to reference a certain URL.<br><br>
15677  * <p>
15678  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15679  * from which the running page was served.<br><br>
15680  * <p>
15681  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15682  * <p>
15683  * Be aware that to enable the browser to parse an XML document, the server must set
15684  * the Content-Type header in the HTTP response to "text/xml".
15685  * @constructor
15686  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15687  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15688  * will be used to make the request.
15689  */
15690 Roo.data.HttpProxy = function(conn){
15691     Roo.data.HttpProxy.superclass.constructor.call(this);
15692     // is conn a conn config or a real conn?
15693     this.conn = conn;
15694     this.useAjax = !conn || !conn.events;
15695   
15696 };
15697
15698 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15699     // thse are take from connection...
15700     
15701     /**
15702      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15703      */
15704     /**
15705      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15706      * extra parameters to each request made by this object. (defaults to undefined)
15707      */
15708     /**
15709      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15710      *  to each request made by this object. (defaults to undefined)
15711      */
15712     /**
15713      * @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)
15714      */
15715     /**
15716      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15717      */
15718      /**
15719      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15720      * @type Boolean
15721      */
15722   
15723
15724     /**
15725      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15726      * @type Boolean
15727      */
15728     /**
15729      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15730      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15731      * a finer-grained basis than the DataProxy events.
15732      */
15733     getConnection : function(){
15734         return this.useAjax ? Roo.Ajax : this.conn;
15735     },
15736
15737     /**
15738      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15739      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15740      * process that block using the passed callback.
15741      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15742      * for the request to the remote server.
15743      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15744      * object into a block of Roo.data.Records.
15745      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15746      * The function must be passed <ul>
15747      * <li>The Record block object</li>
15748      * <li>The "arg" argument from the load function</li>
15749      * <li>A boolean success indicator</li>
15750      * </ul>
15751      * @param {Object} scope The scope in which to call the callback
15752      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15753      */
15754     load : function(params, reader, callback, scope, arg){
15755         if(this.fireEvent("beforeload", this, params) !== false){
15756             var  o = {
15757                 params : params || {},
15758                 request: {
15759                     callback : callback,
15760                     scope : scope,
15761                     arg : arg
15762                 },
15763                 reader: reader,
15764                 callback : this.loadResponse,
15765                 scope: this
15766             };
15767             if(this.useAjax){
15768                 Roo.applyIf(o, this.conn);
15769                 if(this.activeRequest){
15770                     Roo.Ajax.abort(this.activeRequest);
15771                 }
15772                 this.activeRequest = Roo.Ajax.request(o);
15773             }else{
15774                 this.conn.request(o);
15775             }
15776         }else{
15777             callback.call(scope||this, null, arg, false);
15778         }
15779     },
15780
15781     // private
15782     loadResponse : function(o, success, response){
15783         delete this.activeRequest;
15784         if(!success){
15785             this.fireEvent("loadexception", this, o, response);
15786             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15787             return;
15788         }
15789         var result;
15790         try {
15791             result = o.reader.read(response);
15792         }catch(e){
15793             this.fireEvent("loadexception", this, o, response, e);
15794             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15795             return;
15796         }
15797         
15798         this.fireEvent("load", this, o, o.request.arg);
15799         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15800     },
15801
15802     // private
15803     update : function(dataSet){
15804
15805     },
15806
15807     // private
15808     updateResponse : function(dataSet){
15809
15810     }
15811 });/*
15812  * Based on:
15813  * Ext JS Library 1.1.1
15814  * Copyright(c) 2006-2007, Ext JS, LLC.
15815  *
15816  * Originally Released Under LGPL - original licence link has changed is not relivant.
15817  *
15818  * Fork - LGPL
15819  * <script type="text/javascript">
15820  */
15821
15822 /**
15823  * @class Roo.data.ScriptTagProxy
15824  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15825  * other than the originating domain of the running page.<br><br>
15826  * <p>
15827  * <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
15828  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15829  * <p>
15830  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15831  * source code that is used as the source inside a &lt;script> tag.<br><br>
15832  * <p>
15833  * In order for the browser to process the returned data, the server must wrap the data object
15834  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15835  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15836  * depending on whether the callback name was passed:
15837  * <p>
15838  * <pre><code>
15839 boolean scriptTag = false;
15840 String cb = request.getParameter("callback");
15841 if (cb != null) {
15842     scriptTag = true;
15843     response.setContentType("text/javascript");
15844 } else {
15845     response.setContentType("application/x-json");
15846 }
15847 Writer out = response.getWriter();
15848 if (scriptTag) {
15849     out.write(cb + "(");
15850 }
15851 out.print(dataBlock.toJsonString());
15852 if (scriptTag) {
15853     out.write(");");
15854 }
15855 </pre></code>
15856  *
15857  * @constructor
15858  * @param {Object} config A configuration object.
15859  */
15860 Roo.data.ScriptTagProxy = function(config){
15861     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15862     Roo.apply(this, config);
15863     this.head = document.getElementsByTagName("head")[0];
15864 };
15865
15866 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15867
15868 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15869     /**
15870      * @cfg {String} url The URL from which to request the data object.
15871      */
15872     /**
15873      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15874      */
15875     timeout : 30000,
15876     /**
15877      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15878      * the server the name of the callback function set up by the load call to process the returned data object.
15879      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15880      * javascript output which calls this named function passing the data object as its only parameter.
15881      */
15882     callbackParam : "callback",
15883     /**
15884      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15885      * name to the request.
15886      */
15887     nocache : true,
15888
15889     /**
15890      * Load data from the configured URL, read the data object into
15891      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15892      * process that block using the passed callback.
15893      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15894      * for the request to the remote server.
15895      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15896      * object into a block of Roo.data.Records.
15897      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15898      * The function must be passed <ul>
15899      * <li>The Record block object</li>
15900      * <li>The "arg" argument from the load function</li>
15901      * <li>A boolean success indicator</li>
15902      * </ul>
15903      * @param {Object} scope The scope in which to call the callback
15904      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15905      */
15906     load : function(params, reader, callback, scope, arg){
15907         if(this.fireEvent("beforeload", this, params) !== false){
15908
15909             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15910
15911             var url = this.url;
15912             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15913             if(this.nocache){
15914                 url += "&_dc=" + (new Date().getTime());
15915             }
15916             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15917             var trans = {
15918                 id : transId,
15919                 cb : "stcCallback"+transId,
15920                 scriptId : "stcScript"+transId,
15921                 params : params,
15922                 arg : arg,
15923                 url : url,
15924                 callback : callback,
15925                 scope : scope,
15926                 reader : reader
15927             };
15928             var conn = this;
15929
15930             window[trans.cb] = function(o){
15931                 conn.handleResponse(o, trans);
15932             };
15933
15934             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15935
15936             if(this.autoAbort !== false){
15937                 this.abort();
15938             }
15939
15940             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15941
15942             var script = document.createElement("script");
15943             script.setAttribute("src", url);
15944             script.setAttribute("type", "text/javascript");
15945             script.setAttribute("id", trans.scriptId);
15946             this.head.appendChild(script);
15947
15948             this.trans = trans;
15949         }else{
15950             callback.call(scope||this, null, arg, false);
15951         }
15952     },
15953
15954     // private
15955     isLoading : function(){
15956         return this.trans ? true : false;
15957     },
15958
15959     /**
15960      * Abort the current server request.
15961      */
15962     abort : function(){
15963         if(this.isLoading()){
15964             this.destroyTrans(this.trans);
15965         }
15966     },
15967
15968     // private
15969     destroyTrans : function(trans, isLoaded){
15970         this.head.removeChild(document.getElementById(trans.scriptId));
15971         clearTimeout(trans.timeoutId);
15972         if(isLoaded){
15973             window[trans.cb] = undefined;
15974             try{
15975                 delete window[trans.cb];
15976             }catch(e){}
15977         }else{
15978             // if hasn't been loaded, wait for load to remove it to prevent script error
15979             window[trans.cb] = function(){
15980                 window[trans.cb] = undefined;
15981                 try{
15982                     delete window[trans.cb];
15983                 }catch(e){}
15984             };
15985         }
15986     },
15987
15988     // private
15989     handleResponse : function(o, trans){
15990         this.trans = false;
15991         this.destroyTrans(trans, true);
15992         var result;
15993         try {
15994             result = trans.reader.readRecords(o);
15995         }catch(e){
15996             this.fireEvent("loadexception", this, o, trans.arg, e);
15997             trans.callback.call(trans.scope||window, null, trans.arg, false);
15998             return;
15999         }
16000         this.fireEvent("load", this, o, trans.arg);
16001         trans.callback.call(trans.scope||window, result, trans.arg, true);
16002     },
16003
16004     // private
16005     handleFailure : function(trans){
16006         this.trans = false;
16007         this.destroyTrans(trans, false);
16008         this.fireEvent("loadexception", this, null, trans.arg);
16009         trans.callback.call(trans.scope||window, null, trans.arg, false);
16010     }
16011 });/*
16012  * Based on:
16013  * Ext JS Library 1.1.1
16014  * Copyright(c) 2006-2007, Ext JS, LLC.
16015  *
16016  * Originally Released Under LGPL - original licence link has changed is not relivant.
16017  *
16018  * Fork - LGPL
16019  * <script type="text/javascript">
16020  */
16021
16022 /**
16023  * @class Roo.data.JsonReader
16024  * @extends Roo.data.DataReader
16025  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16026  * based on mappings in a provided Roo.data.Record constructor.
16027  * 
16028  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16029  * in the reply previously. 
16030  * 
16031  * <p>
16032  * Example code:
16033  * <pre><code>
16034 var RecordDef = Roo.data.Record.create([
16035     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16036     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16037 ]);
16038 var myReader = new Roo.data.JsonReader({
16039     totalProperty: "results",    // The property which contains the total dataset size (optional)
16040     root: "rows",                // The property which contains an Array of row objects
16041     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16042 }, RecordDef);
16043 </code></pre>
16044  * <p>
16045  * This would consume a JSON file like this:
16046  * <pre><code>
16047 { 'results': 2, 'rows': [
16048     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16049     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16050 }
16051 </code></pre>
16052  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16053  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16054  * paged from the remote server.
16055  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16056  * @cfg {String} root name of the property which contains the Array of row objects.
16057  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16058  * @cfg {Array} fields Array of field definition objects
16059  * @constructor
16060  * Create a new JsonReader
16061  * @param {Object} meta Metadata configuration options
16062  * @param {Object} recordType Either an Array of field definition objects,
16063  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16064  */
16065 Roo.data.JsonReader = function(meta, recordType){
16066     
16067     meta = meta || {};
16068     // set some defaults:
16069     Roo.applyIf(meta, {
16070         totalProperty: 'total',
16071         successProperty : 'success',
16072         root : 'data',
16073         id : 'id'
16074     });
16075     
16076     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16077 };
16078 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16079     
16080     readerType : 'Json',
16081     
16082     /**
16083      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16084      * Used by Store query builder to append _requestMeta to params.
16085      * 
16086      */
16087     metaFromRemote : false,
16088     /**
16089      * This method is only used by a DataProxy which has retrieved data from a remote server.
16090      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16091      * @return {Object} data A data block which is used by an Roo.data.Store object as
16092      * a cache of Roo.data.Records.
16093      */
16094     read : function(response){
16095         var json = response.responseText;
16096        
16097         var o = /* eval:var:o */ eval("("+json+")");
16098         if(!o) {
16099             throw {message: "JsonReader.read: Json object not found"};
16100         }
16101         
16102         if(o.metaData){
16103             
16104             delete this.ef;
16105             this.metaFromRemote = true;
16106             this.meta = o.metaData;
16107             this.recordType = Roo.data.Record.create(o.metaData.fields);
16108             this.onMetaChange(this.meta, this.recordType, o);
16109         }
16110         return this.readRecords(o);
16111     },
16112
16113     // private function a store will implement
16114     onMetaChange : function(meta, recordType, o){
16115
16116     },
16117
16118     /**
16119          * @ignore
16120          */
16121     simpleAccess: function(obj, subsc) {
16122         return obj[subsc];
16123     },
16124
16125         /**
16126          * @ignore
16127          */
16128     getJsonAccessor: function(){
16129         var re = /[\[\.]/;
16130         return function(expr) {
16131             try {
16132                 return(re.test(expr))
16133                     ? new Function("obj", "return obj." + expr)
16134                     : function(obj){
16135                         return obj[expr];
16136                     };
16137             } catch(e){}
16138             return Roo.emptyFn;
16139         };
16140     }(),
16141
16142     /**
16143      * Create a data block containing Roo.data.Records from an XML document.
16144      * @param {Object} o An object which contains an Array of row objects in the property specified
16145      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16146      * which contains the total size of the dataset.
16147      * @return {Object} data A data block which is used by an Roo.data.Store object as
16148      * a cache of Roo.data.Records.
16149      */
16150     readRecords : function(o){
16151         /**
16152          * After any data loads, the raw JSON data is available for further custom processing.
16153          * @type Object
16154          */
16155         this.o = o;
16156         var s = this.meta, Record = this.recordType,
16157             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16158
16159 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16160         if (!this.ef) {
16161             if(s.totalProperty) {
16162                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16163                 }
16164                 if(s.successProperty) {
16165                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16166                 }
16167                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16168                 if (s.id) {
16169                         var g = this.getJsonAccessor(s.id);
16170                         this.getId = function(rec) {
16171                                 var r = g(rec);  
16172                                 return (r === undefined || r === "") ? null : r;
16173                         };
16174                 } else {
16175                         this.getId = function(){return null;};
16176                 }
16177             this.ef = [];
16178             for(var jj = 0; jj < fl; jj++){
16179                 f = fi[jj];
16180                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16181                 this.ef[jj] = this.getJsonAccessor(map);
16182             }
16183         }
16184
16185         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16186         if(s.totalProperty){
16187             var vt = parseInt(this.getTotal(o), 10);
16188             if(!isNaN(vt)){
16189                 totalRecords = vt;
16190             }
16191         }
16192         if(s.successProperty){
16193             var vs = this.getSuccess(o);
16194             if(vs === false || vs === 'false'){
16195                 success = false;
16196             }
16197         }
16198         var records = [];
16199         for(var i = 0; i < c; i++){
16200                 var n = root[i];
16201             var values = {};
16202             var id = this.getId(n);
16203             for(var j = 0; j < fl; j++){
16204                 f = fi[j];
16205             var v = this.ef[j](n);
16206             if (!f.convert) {
16207                 Roo.log('missing convert for ' + f.name);
16208                 Roo.log(f);
16209                 continue;
16210             }
16211             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16212             }
16213             var record = new Record(values, id);
16214             record.json = n;
16215             records[i] = record;
16216         }
16217         return {
16218             raw : o,
16219             success : success,
16220             records : records,
16221             totalRecords : totalRecords
16222         };
16223     },
16224     // used when loading children.. @see loadDataFromChildren
16225     toLoadData: function(rec)
16226     {
16227         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16228         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16229         return { data : data, total : data.length };
16230         
16231     }
16232 });/*
16233  * Based on:
16234  * Ext JS Library 1.1.1
16235  * Copyright(c) 2006-2007, Ext JS, LLC.
16236  *
16237  * Originally Released Under LGPL - original licence link has changed is not relivant.
16238  *
16239  * Fork - LGPL
16240  * <script type="text/javascript">
16241  */
16242
16243 /**
16244  * @class Roo.data.ArrayReader
16245  * @extends Roo.data.DataReader
16246  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16247  * Each element of that Array represents a row of data fields. The
16248  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16249  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16250  * <p>
16251  * Example code:.
16252  * <pre><code>
16253 var RecordDef = Roo.data.Record.create([
16254     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16255     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16256 ]);
16257 var myReader = new Roo.data.ArrayReader({
16258     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16259 }, RecordDef);
16260 </code></pre>
16261  * <p>
16262  * This would consume an Array like this:
16263  * <pre><code>
16264 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16265   </code></pre>
16266  
16267  * @constructor
16268  * Create a new JsonReader
16269  * @param {Object} meta Metadata configuration options.
16270  * @param {Object|Array} recordType Either an Array of field definition objects
16271  * 
16272  * @cfg {Array} fields Array of field definition objects
16273  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16274  * as specified to {@link Roo.data.Record#create},
16275  * or an {@link Roo.data.Record} object
16276  *
16277  * 
16278  * created using {@link Roo.data.Record#create}.
16279  */
16280 Roo.data.ArrayReader = function(meta, recordType)
16281 {    
16282     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16283 };
16284
16285 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16286     
16287       /**
16288      * Create a data block containing Roo.data.Records from an XML document.
16289      * @param {Object} o An Array of row objects which represents the dataset.
16290      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16291      * a cache of Roo.data.Records.
16292      */
16293     readRecords : function(o)
16294     {
16295         var sid = this.meta ? this.meta.id : null;
16296         var recordType = this.recordType, fields = recordType.prototype.fields;
16297         var records = [];
16298         var root = o;
16299         for(var i = 0; i < root.length; i++){
16300             var n = root[i];
16301             var values = {};
16302             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16303             for(var j = 0, jlen = fields.length; j < jlen; j++){
16304                 var f = fields.items[j];
16305                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16306                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16307                 v = f.convert(v);
16308                 values[f.name] = v;
16309             }
16310             var record = new recordType(values, id);
16311             record.json = n;
16312             records[records.length] = record;
16313         }
16314         return {
16315             records : records,
16316             totalRecords : records.length
16317         };
16318     },
16319     // used when loading children.. @see loadDataFromChildren
16320     toLoadData: function(rec)
16321     {
16322         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16323         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16324         
16325     }
16326     
16327     
16328 });/*
16329  * - LGPL
16330  * * 
16331  */
16332
16333 /**
16334  * @class Roo.bootstrap.ComboBox
16335  * @extends Roo.bootstrap.TriggerField
16336  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16337  * @cfg {Boolean} append (true|false) default false
16338  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16339  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16340  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16341  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16342  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16343  * @cfg {Boolean} animate default true
16344  * @cfg {Boolean} emptyResultText only for touch device
16345  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16346  * @cfg {String} emptyTitle default ''
16347  * @cfg {Number} width fixed with? experimental
16348  * @constructor
16349  * Create a new ComboBox.
16350  * @param {Object} config Configuration options
16351  */
16352 Roo.bootstrap.ComboBox = function(config){
16353     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16354     this.addEvents({
16355         /**
16356          * @event expand
16357          * Fires when the dropdown list is expanded
16358         * @param {Roo.bootstrap.ComboBox} combo This combo box
16359         */
16360         'expand' : true,
16361         /**
16362          * @event collapse
16363          * Fires when the dropdown list is collapsed
16364         * @param {Roo.bootstrap.ComboBox} combo This combo box
16365         */
16366         'collapse' : true,
16367         /**
16368          * @event beforeselect
16369          * Fires before a list item is selected. Return false to cancel the selection.
16370         * @param {Roo.bootstrap.ComboBox} combo This combo box
16371         * @param {Roo.data.Record} record The data record returned from the underlying store
16372         * @param {Number} index The index of the selected item in the dropdown list
16373         */
16374         'beforeselect' : true,
16375         /**
16376          * @event select
16377          * Fires when a list item is selected
16378         * @param {Roo.bootstrap.ComboBox} combo This combo box
16379         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16380         * @param {Number} index The index of the selected item in the dropdown list
16381         */
16382         'select' : true,
16383         /**
16384          * @event beforequery
16385          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16386          * The event object passed has these properties:
16387         * @param {Roo.bootstrap.ComboBox} combo This combo box
16388         * @param {String} query The query
16389         * @param {Boolean} forceAll true to force "all" query
16390         * @param {Boolean} cancel true to cancel the query
16391         * @param {Object} e The query event object
16392         */
16393         'beforequery': true,
16394          /**
16395          * @event add
16396          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16397         * @param {Roo.bootstrap.ComboBox} combo This combo box
16398         */
16399         'add' : true,
16400         /**
16401          * @event edit
16402          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16403         * @param {Roo.bootstrap.ComboBox} combo This combo box
16404         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16405         */
16406         'edit' : true,
16407         /**
16408          * @event remove
16409          * Fires when the remove value from the combobox array
16410         * @param {Roo.bootstrap.ComboBox} combo This combo box
16411         */
16412         'remove' : true,
16413         /**
16414          * @event afterremove
16415          * Fires when the remove value from the combobox array
16416         * @param {Roo.bootstrap.ComboBox} combo This combo box
16417         */
16418         'afterremove' : true,
16419         /**
16420          * @event specialfilter
16421          * Fires when specialfilter
16422             * @param {Roo.bootstrap.ComboBox} combo This combo box
16423             */
16424         'specialfilter' : true,
16425         /**
16426          * @event tick
16427          * Fires when tick the element
16428             * @param {Roo.bootstrap.ComboBox} combo This combo box
16429             */
16430         'tick' : true,
16431         /**
16432          * @event touchviewdisplay
16433          * Fires when touch view require special display (default is using displayField)
16434             * @param {Roo.bootstrap.ComboBox} combo This combo box
16435             * @param {Object} cfg set html .
16436             */
16437         'touchviewdisplay' : true
16438         
16439     });
16440     
16441     this.item = [];
16442     this.tickItems = [];
16443     
16444     this.selectedIndex = -1;
16445     if(this.mode == 'local'){
16446         if(config.queryDelay === undefined){
16447             this.queryDelay = 10;
16448         }
16449         if(config.minChars === undefined){
16450             this.minChars = 0;
16451         }
16452     }
16453 };
16454
16455 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16456      
16457     /**
16458      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16459      * rendering into an Roo.Editor, defaults to false)
16460      */
16461     /**
16462      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16463      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16464      */
16465     /**
16466      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16467      */
16468     /**
16469      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16470      * the dropdown list (defaults to undefined, with no header element)
16471      */
16472
16473      /**
16474      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16475      */
16476      
16477      /**
16478      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16479      */
16480     listWidth: undefined,
16481     /**
16482      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16483      * mode = 'remote' or 'text' if mode = 'local')
16484      */
16485     displayField: undefined,
16486     
16487     /**
16488      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16489      * mode = 'remote' or 'value' if mode = 'local'). 
16490      * Note: use of a valueField requires the user make a selection
16491      * in order for a value to be mapped.
16492      */
16493     valueField: undefined,
16494     /**
16495      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16496      */
16497     modalTitle : '',
16498     
16499     /**
16500      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16501      * field's data value (defaults to the underlying DOM element's name)
16502      */
16503     hiddenName: undefined,
16504     /**
16505      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16506      */
16507     listClass: '',
16508     /**
16509      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16510      */
16511     selectedClass: 'active',
16512     
16513     /**
16514      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16515      */
16516     shadow:'sides',
16517     /**
16518      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16519      * anchor positions (defaults to 'tl-bl')
16520      */
16521     listAlign: 'tl-bl?',
16522     /**
16523      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16524      */
16525     maxHeight: 300,
16526     /**
16527      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16528      * query specified by the allQuery config option (defaults to 'query')
16529      */
16530     triggerAction: 'query',
16531     /**
16532      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16533      * (defaults to 4, does not apply if editable = false)
16534      */
16535     minChars : 4,
16536     /**
16537      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16538      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16539      */
16540     typeAhead: false,
16541     /**
16542      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16543      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16544      */
16545     queryDelay: 500,
16546     /**
16547      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16548      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16549      */
16550     pageSize: 0,
16551     /**
16552      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16553      * when editable = true (defaults to false)
16554      */
16555     selectOnFocus:false,
16556     /**
16557      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16558      */
16559     queryParam: 'query',
16560     /**
16561      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16562      * when mode = 'remote' (defaults to 'Loading...')
16563      */
16564     loadingText: 'Loading...',
16565     /**
16566      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16567      */
16568     resizable: false,
16569     /**
16570      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16571      */
16572     handleHeight : 8,
16573     /**
16574      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16575      * traditional select (defaults to true)
16576      */
16577     editable: true,
16578     /**
16579      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16580      */
16581     allQuery: '',
16582     /**
16583      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16584      */
16585     mode: 'remote',
16586     /**
16587      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16588      * listWidth has a higher value)
16589      */
16590     minListWidth : 70,
16591     /**
16592      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16593      * allow the user to set arbitrary text into the field (defaults to false)
16594      */
16595     forceSelection:false,
16596     /**
16597      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16598      * if typeAhead = true (defaults to 250)
16599      */
16600     typeAheadDelay : 250,
16601     /**
16602      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16603      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16604      */
16605     valueNotFoundText : undefined,
16606     /**
16607      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16608      */
16609     blockFocus : false,
16610     
16611     /**
16612      * @cfg {Boolean} disableClear Disable showing of clear button.
16613      */
16614     disableClear : false,
16615     /**
16616      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16617      */
16618     alwaysQuery : false,
16619     
16620     /**
16621      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16622      */
16623     multiple : false,
16624     
16625     /**
16626      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16627      */
16628     invalidClass : "has-warning",
16629     
16630     /**
16631      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16632      */
16633     validClass : "has-success",
16634     
16635     /**
16636      * @cfg {Boolean} specialFilter (true|false) special filter default false
16637      */
16638     specialFilter : false,
16639     
16640     /**
16641      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16642      */
16643     mobileTouchView : true,
16644     
16645     /**
16646      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16647      */
16648     useNativeIOS : false,
16649     
16650     /**
16651      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16652      */
16653     mobile_restrict_height : false,
16654     
16655     ios_options : false,
16656     
16657     //private
16658     addicon : false,
16659     editicon: false,
16660     
16661     page: 0,
16662     hasQuery: false,
16663     append: false,
16664     loadNext: false,
16665     autoFocus : true,
16666     tickable : false,
16667     btnPosition : 'right',
16668     triggerList : true,
16669     showToggleBtn : true,
16670     animate : true,
16671     emptyResultText: 'Empty',
16672     triggerText : 'Select',
16673     emptyTitle : '',
16674     width : false,
16675     
16676     // element that contains real text value.. (when hidden is used..)
16677     
16678     getAutoCreate : function()
16679     {   
16680         var cfg = false;
16681         //render
16682         /*
16683          * Render classic select for iso
16684          */
16685         
16686         if(Roo.isIOS && this.useNativeIOS){
16687             cfg = this.getAutoCreateNativeIOS();
16688             return cfg;
16689         }
16690         
16691         /*
16692          * Touch Devices
16693          */
16694         
16695         if(Roo.isTouch && this.mobileTouchView){
16696             cfg = this.getAutoCreateTouchView();
16697             return cfg;;
16698         }
16699         
16700         /*
16701          *  Normal ComboBox
16702          */
16703         if(!this.tickable){
16704             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16705             return cfg;
16706         }
16707         
16708         /*
16709          *  ComboBox with tickable selections
16710          */
16711              
16712         var align = this.labelAlign || this.parentLabelAlign();
16713         
16714         cfg = {
16715             cls : 'form-group roo-combobox-tickable' //input-group
16716         };
16717         
16718         var btn_text_select = '';
16719         var btn_text_done = '';
16720         var btn_text_cancel = '';
16721         
16722         if (this.btn_text_show) {
16723             btn_text_select = 'Select';
16724             btn_text_done = 'Done';
16725             btn_text_cancel = 'Cancel'; 
16726         }
16727         
16728         var buttons = {
16729             tag : 'div',
16730             cls : 'tickable-buttons',
16731             cn : [
16732                 {
16733                     tag : 'button',
16734                     type : 'button',
16735                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16736                     //html : this.triggerText
16737                     html: btn_text_select
16738                 },
16739                 {
16740                     tag : 'button',
16741                     type : 'button',
16742                     name : 'ok',
16743                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16744                     //html : 'Done'
16745                     html: btn_text_done
16746                 },
16747                 {
16748                     tag : 'button',
16749                     type : 'button',
16750                     name : 'cancel',
16751                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16752                     //html : 'Cancel'
16753                     html: btn_text_cancel
16754                 }
16755             ]
16756         };
16757         
16758         if(this.editable){
16759             buttons.cn.unshift({
16760                 tag: 'input',
16761                 cls: 'roo-select2-search-field-input'
16762             });
16763         }
16764         
16765         var _this = this;
16766         
16767         Roo.each(buttons.cn, function(c){
16768             if (_this.size) {
16769                 c.cls += ' btn-' + _this.size;
16770             }
16771
16772             if (_this.disabled) {
16773                 c.disabled = true;
16774             }
16775         });
16776         
16777         var box = {
16778             tag: 'div',
16779             style : 'display: contents',
16780             cn: [
16781                 {
16782                     tag: 'input',
16783                     type : 'hidden',
16784                     cls: 'form-hidden-field'
16785                 },
16786                 {
16787                     tag: 'ul',
16788                     cls: 'roo-select2-choices',
16789                     cn:[
16790                         {
16791                             tag: 'li',
16792                             cls: 'roo-select2-search-field',
16793                             cn: [
16794                                 buttons
16795                             ]
16796                         }
16797                     ]
16798                 }
16799             ]
16800         };
16801         
16802         var combobox = {
16803             cls: 'roo-select2-container input-group roo-select2-container-multi',
16804             cn: [
16805                 
16806                 box
16807 //                {
16808 //                    tag: 'ul',
16809 //                    cls: 'typeahead typeahead-long dropdown-menu',
16810 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16811 //                }
16812             ]
16813         };
16814         
16815         if(this.hasFeedback && !this.allowBlank){
16816             
16817             var feedback = {
16818                 tag: 'span',
16819                 cls: 'glyphicon form-control-feedback'
16820             };
16821
16822             combobox.cn.push(feedback);
16823         }
16824         
16825         
16826         
16827         var indicator = {
16828             tag : 'i',
16829             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16830             tooltip : 'This field is required'
16831         };
16832         if (Roo.bootstrap.version == 4) {
16833             indicator = {
16834                 tag : 'i',
16835                 style : 'display:none'
16836             };
16837         }
16838         if (align ==='left' && this.fieldLabel.length) {
16839             
16840             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16841             
16842             cfg.cn = [
16843                 indicator,
16844                 {
16845                     tag: 'label',
16846                     'for' :  id,
16847                     cls : 'control-label col-form-label',
16848                     html : this.fieldLabel
16849
16850                 },
16851                 {
16852                     cls : "", 
16853                     cn: [
16854                         combobox
16855                     ]
16856                 }
16857
16858             ];
16859             
16860             var labelCfg = cfg.cn[1];
16861             var contentCfg = cfg.cn[2];
16862             
16863
16864             if(this.indicatorpos == 'right'){
16865                 
16866                 cfg.cn = [
16867                     {
16868                         tag: 'label',
16869                         'for' :  id,
16870                         cls : 'control-label col-form-label',
16871                         cn : [
16872                             {
16873                                 tag : 'span',
16874                                 html : this.fieldLabel
16875                             },
16876                             indicator
16877                         ]
16878                     },
16879                     {
16880                         cls : "",
16881                         cn: [
16882                             combobox
16883                         ]
16884                     }
16885
16886                 ];
16887                 
16888                 
16889                 
16890                 labelCfg = cfg.cn[0];
16891                 contentCfg = cfg.cn[1];
16892             
16893             }
16894             
16895             if(this.labelWidth > 12){
16896                 labelCfg.style = "width: " + this.labelWidth + 'px';
16897             }
16898             if(this.width * 1 > 0){
16899                 contentCfg.style = "width: " + this.width + 'px';
16900             }
16901             if(this.labelWidth < 13 && this.labelmd == 0){
16902                 this.labelmd = this.labelWidth;
16903             }
16904             
16905             if(this.labellg > 0){
16906                 labelCfg.cls += ' col-lg-' + this.labellg;
16907                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16908             }
16909             
16910             if(this.labelmd > 0){
16911                 labelCfg.cls += ' col-md-' + this.labelmd;
16912                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16913             }
16914             
16915             if(this.labelsm > 0){
16916                 labelCfg.cls += ' col-sm-' + this.labelsm;
16917                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16918             }
16919             
16920             if(this.labelxs > 0){
16921                 labelCfg.cls += ' col-xs-' + this.labelxs;
16922                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16923             }
16924                 
16925                 
16926         } else if ( this.fieldLabel.length) {
16927 //                Roo.log(" label");
16928                  cfg.cn = [
16929                    indicator,
16930                     {
16931                         tag: 'label',
16932                         //cls : 'input-group-addon',
16933                         html : this.fieldLabel
16934                     },
16935                     combobox
16936                 ];
16937                 
16938                 if(this.indicatorpos == 'right'){
16939                     cfg.cn = [
16940                         {
16941                             tag: 'label',
16942                             //cls : 'input-group-addon',
16943                             html : this.fieldLabel
16944                         },
16945                         indicator,
16946                         combobox
16947                     ];
16948                     
16949                 }
16950
16951         } else {
16952             
16953 //                Roo.log(" no label && no align");
16954                 cfg = combobox
16955                      
16956                 
16957         }
16958          
16959         var settings=this;
16960         ['xs','sm','md','lg'].map(function(size){
16961             if (settings[size]) {
16962                 cfg.cls += ' col-' + size + '-' + settings[size];
16963             }
16964         });
16965         
16966         return cfg;
16967         
16968     },
16969     
16970     _initEventsCalled : false,
16971     
16972     // private
16973     initEvents: function()
16974     {   
16975         if (this._initEventsCalled) { // as we call render... prevent looping...
16976             return;
16977         }
16978         this._initEventsCalled = true;
16979         
16980         if (!this.store) {
16981             throw "can not find store for combo";
16982         }
16983         
16984         this.indicator = this.indicatorEl();
16985         
16986         this.store = Roo.factory(this.store, Roo.data);
16987         this.store.parent = this;
16988         
16989         // if we are building from html. then this element is so complex, that we can not really
16990         // use the rendered HTML.
16991         // so we have to trash and replace the previous code.
16992         if (Roo.XComponent.build_from_html) {
16993             // remove this element....
16994             var e = this.el.dom, k=0;
16995             while (e ) { e = e.previousSibling;  ++k;}
16996
16997             this.el.remove();
16998             
16999             this.el=false;
17000             this.rendered = false;
17001             
17002             this.render(this.parent().getChildContainer(true), k);
17003         }
17004         
17005         if(Roo.isIOS && this.useNativeIOS){
17006             this.initIOSView();
17007             return;
17008         }
17009         
17010         /*
17011          * Touch Devices
17012          */
17013         
17014         if(Roo.isTouch && this.mobileTouchView){
17015             this.initTouchView();
17016             return;
17017         }
17018         
17019         if(this.tickable){
17020             this.initTickableEvents();
17021             return;
17022         }
17023         
17024         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17025         
17026         if(this.hiddenName){
17027             
17028             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17029             
17030             this.hiddenField.dom.value =
17031                 this.hiddenValue !== undefined ? this.hiddenValue :
17032                 this.value !== undefined ? this.value : '';
17033
17034             // prevent input submission
17035             this.el.dom.removeAttribute('name');
17036             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17037              
17038              
17039         }
17040         //if(Roo.isGecko){
17041         //    this.el.dom.setAttribute('autocomplete', 'off');
17042         //}
17043         
17044         var cls = 'x-combo-list';
17045         
17046         //this.list = new Roo.Layer({
17047         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17048         //});
17049         
17050         var _this = this;
17051         
17052         (function(){
17053             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17054             _this.list.setWidth(lw);
17055         }).defer(100);
17056         
17057         this.list.on('mouseover', this.onViewOver, this);
17058         this.list.on('mousemove', this.onViewMove, this);
17059         this.list.on('scroll', this.onViewScroll, this);
17060         
17061         /*
17062         this.list.swallowEvent('mousewheel');
17063         this.assetHeight = 0;
17064
17065         if(this.title){
17066             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17067             this.assetHeight += this.header.getHeight();
17068         }
17069
17070         this.innerList = this.list.createChild({cls:cls+'-inner'});
17071         this.innerList.on('mouseover', this.onViewOver, this);
17072         this.innerList.on('mousemove', this.onViewMove, this);
17073         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17074         
17075         if(this.allowBlank && !this.pageSize && !this.disableClear){
17076             this.footer = this.list.createChild({cls:cls+'-ft'});
17077             this.pageTb = new Roo.Toolbar(this.footer);
17078            
17079         }
17080         if(this.pageSize){
17081             this.footer = this.list.createChild({cls:cls+'-ft'});
17082             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17083                     {pageSize: this.pageSize});
17084             
17085         }
17086         
17087         if (this.pageTb && this.allowBlank && !this.disableClear) {
17088             var _this = this;
17089             this.pageTb.add(new Roo.Toolbar.Fill(), {
17090                 cls: 'x-btn-icon x-btn-clear',
17091                 text: '&#160;',
17092                 handler: function()
17093                 {
17094                     _this.collapse();
17095                     _this.clearValue();
17096                     _this.onSelect(false, -1);
17097                 }
17098             });
17099         }
17100         if (this.footer) {
17101             this.assetHeight += this.footer.getHeight();
17102         }
17103         */
17104             
17105         if(!this.tpl){
17106             this.tpl = Roo.bootstrap.version == 4 ?
17107                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17108                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17109         }
17110
17111         this.view = new Roo.View(this.list, this.tpl, {
17112             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17113         });
17114         //this.view.wrapEl.setDisplayed(false);
17115         this.view.on('click', this.onViewClick, this);
17116         
17117         
17118         this.store.on('beforeload', this.onBeforeLoad, this);
17119         this.store.on('load', this.onLoad, this);
17120         this.store.on('loadexception', this.onLoadException, this);
17121         /*
17122         if(this.resizable){
17123             this.resizer = new Roo.Resizable(this.list,  {
17124                pinned:true, handles:'se'
17125             });
17126             this.resizer.on('resize', function(r, w, h){
17127                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17128                 this.listWidth = w;
17129                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17130                 this.restrictHeight();
17131             }, this);
17132             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17133         }
17134         */
17135         if(!this.editable){
17136             this.editable = true;
17137             this.setEditable(false);
17138         }
17139         
17140         /*
17141         
17142         if (typeof(this.events.add.listeners) != 'undefined') {
17143             
17144             this.addicon = this.wrap.createChild(
17145                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17146        
17147             this.addicon.on('click', function(e) {
17148                 this.fireEvent('add', this);
17149             }, this);
17150         }
17151         if (typeof(this.events.edit.listeners) != 'undefined') {
17152             
17153             this.editicon = this.wrap.createChild(
17154                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17155             if (this.addicon) {
17156                 this.editicon.setStyle('margin-left', '40px');
17157             }
17158             this.editicon.on('click', function(e) {
17159                 
17160                 // we fire even  if inothing is selected..
17161                 this.fireEvent('edit', this, this.lastData );
17162                 
17163             }, this);
17164         }
17165         */
17166         
17167         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17168             "up" : function(e){
17169                 this.inKeyMode = true;
17170                 this.selectPrev();
17171             },
17172
17173             "down" : function(e){
17174                 if(!this.isExpanded()){
17175                     this.onTriggerClick();
17176                 }else{
17177                     this.inKeyMode = true;
17178                     this.selectNext();
17179                 }
17180             },
17181
17182             "enter" : function(e){
17183 //                this.onViewClick();
17184                 //return true;
17185                 this.collapse();
17186                 
17187                 if(this.fireEvent("specialkey", this, e)){
17188                     this.onViewClick(false);
17189                 }
17190                 
17191                 return true;
17192             },
17193
17194             "esc" : function(e){
17195                 this.collapse();
17196             },
17197
17198             "tab" : function(e){
17199                 this.collapse();
17200                 
17201                 if(this.fireEvent("specialkey", this, e)){
17202                     this.onViewClick(false);
17203                 }
17204                 
17205                 return true;
17206             },
17207
17208             scope : this,
17209
17210             doRelay : function(foo, bar, hname){
17211                 if(hname == 'down' || this.scope.isExpanded()){
17212                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17213                 }
17214                 return true;
17215             },
17216
17217             forceKeyDown: true
17218         });
17219         
17220         
17221         this.queryDelay = Math.max(this.queryDelay || 10,
17222                 this.mode == 'local' ? 10 : 250);
17223         
17224         
17225         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17226         
17227         if(this.typeAhead){
17228             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17229         }
17230         if(this.editable !== false){
17231             this.inputEl().on("keyup", this.onKeyUp, this);
17232         }
17233         if(this.forceSelection){
17234             this.inputEl().on('blur', this.doForce, this);
17235         }
17236         
17237         if(this.multiple){
17238             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17239             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17240         }
17241     },
17242     
17243     initTickableEvents: function()
17244     {   
17245         this.createList();
17246         
17247         if(this.hiddenName){
17248             
17249             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17250             
17251             this.hiddenField.dom.value =
17252                 this.hiddenValue !== undefined ? this.hiddenValue :
17253                 this.value !== undefined ? this.value : '';
17254
17255             // prevent input submission
17256             this.el.dom.removeAttribute('name');
17257             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17258              
17259              
17260         }
17261         
17262 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17263         
17264         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17265         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17266         if(this.triggerList){
17267             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17268         }
17269          
17270         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17271         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17272         
17273         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17274         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17275         
17276         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17277         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17278         
17279         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17280         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17281         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17282         
17283         this.okBtn.hide();
17284         this.cancelBtn.hide();
17285         
17286         var _this = this;
17287         
17288         (function(){
17289             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17290             _this.list.setWidth(lw);
17291         }).defer(100);
17292         
17293         this.list.on('mouseover', this.onViewOver, this);
17294         this.list.on('mousemove', this.onViewMove, this);
17295         
17296         this.list.on('scroll', this.onViewScroll, this);
17297         
17298         if(!this.tpl){
17299             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17300                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17301         }
17302
17303         this.view = new Roo.View(this.list, this.tpl, {
17304             singleSelect:true,
17305             tickable:true,
17306             parent:this,
17307             store: this.store,
17308             selectedClass: this.selectedClass
17309         });
17310         
17311         //this.view.wrapEl.setDisplayed(false);
17312         this.view.on('click', this.onViewClick, this);
17313         
17314         
17315         
17316         this.store.on('beforeload', this.onBeforeLoad, this);
17317         this.store.on('load', this.onLoad, this);
17318         this.store.on('loadexception', this.onLoadException, this);
17319         
17320         if(this.editable){
17321             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17322                 "up" : function(e){
17323                     this.inKeyMode = true;
17324                     this.selectPrev();
17325                 },
17326
17327                 "down" : function(e){
17328                     this.inKeyMode = true;
17329                     this.selectNext();
17330                 },
17331
17332                 "enter" : function(e){
17333                     if(this.fireEvent("specialkey", this, e)){
17334                         this.onViewClick(false);
17335                     }
17336                     
17337                     return true;
17338                 },
17339
17340                 "esc" : function(e){
17341                     this.onTickableFooterButtonClick(e, false, false);
17342                 },
17343
17344                 "tab" : function(e){
17345                     this.fireEvent("specialkey", this, e);
17346                     
17347                     this.onTickableFooterButtonClick(e, false, false);
17348                     
17349                     return true;
17350                 },
17351
17352                 scope : this,
17353
17354                 doRelay : function(e, fn, key){
17355                     if(this.scope.isExpanded()){
17356                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17357                     }
17358                     return true;
17359                 },
17360
17361                 forceKeyDown: true
17362             });
17363         }
17364         
17365         this.queryDelay = Math.max(this.queryDelay || 10,
17366                 this.mode == 'local' ? 10 : 250);
17367         
17368         
17369         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17370         
17371         if(this.typeAhead){
17372             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17373         }
17374         
17375         if(this.editable !== false){
17376             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17377         }
17378         
17379         this.indicator = this.indicatorEl();
17380         
17381         if(this.indicator){
17382             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17383             this.indicator.hide();
17384         }
17385         
17386     },
17387
17388     onDestroy : function(){
17389         if(this.view){
17390             this.view.setStore(null);
17391             this.view.el.removeAllListeners();
17392             this.view.el.remove();
17393             this.view.purgeListeners();
17394         }
17395         if(this.list){
17396             this.list.dom.innerHTML  = '';
17397         }
17398         
17399         if(this.store){
17400             this.store.un('beforeload', this.onBeforeLoad, this);
17401             this.store.un('load', this.onLoad, this);
17402             this.store.un('loadexception', this.onLoadException, this);
17403         }
17404         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17405     },
17406
17407     // private
17408     fireKey : function(e){
17409         if(e.isNavKeyPress() && !this.list.isVisible()){
17410             this.fireEvent("specialkey", this, e);
17411         }
17412     },
17413
17414     // private
17415     onResize: function(w, h)
17416     {
17417         
17418         
17419 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17420 //        
17421 //        if(typeof w != 'number'){
17422 //            // we do not handle it!?!?
17423 //            return;
17424 //        }
17425 //        var tw = this.trigger.getWidth();
17426 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17427 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17428 //        var x = w - tw;
17429 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17430 //            
17431 //        //this.trigger.setStyle('left', x+'px');
17432 //        
17433 //        if(this.list && this.listWidth === undefined){
17434 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17435 //            this.list.setWidth(lw);
17436 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17437 //        }
17438         
17439     
17440         
17441     },
17442
17443     /**
17444      * Allow or prevent the user from directly editing the field text.  If false is passed,
17445      * the user will only be able to select from the items defined in the dropdown list.  This method
17446      * is the runtime equivalent of setting the 'editable' config option at config time.
17447      * @param {Boolean} value True to allow the user to directly edit the field text
17448      */
17449     setEditable : function(value){
17450         if(value == this.editable){
17451             return;
17452         }
17453         this.editable = value;
17454         if(!value){
17455             this.inputEl().dom.setAttribute('readOnly', true);
17456             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17457             this.inputEl().addClass('x-combo-noedit');
17458         }else{
17459             this.inputEl().dom.removeAttribute('readOnly');
17460             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17461             this.inputEl().removeClass('x-combo-noedit');
17462         }
17463     },
17464
17465     // private
17466     
17467     onBeforeLoad : function(combo,opts){
17468         if(!this.hasFocus){
17469             return;
17470         }
17471          if (!opts.add) {
17472             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17473          }
17474         this.restrictHeight();
17475         this.selectedIndex = -1;
17476     },
17477
17478     // private
17479     onLoad : function(){
17480         
17481         this.hasQuery = false;
17482         
17483         if(!this.hasFocus){
17484             return;
17485         }
17486         
17487         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17488             this.loading.hide();
17489         }
17490         
17491         if(this.store.getCount() > 0){
17492             
17493             this.expand();
17494             this.restrictHeight();
17495             if(this.lastQuery == this.allQuery){
17496                 if(this.editable && !this.tickable){
17497                     this.inputEl().dom.select();
17498                 }
17499                 
17500                 if(
17501                     !this.selectByValue(this.value, true) &&
17502                     this.autoFocus && 
17503                     (
17504                         !this.store.lastOptions ||
17505                         typeof(this.store.lastOptions.add) == 'undefined' || 
17506                         this.store.lastOptions.add != true
17507                     )
17508                 ){
17509                     this.select(0, true);
17510                 }
17511             }else{
17512                 if(this.autoFocus){
17513                     this.selectNext();
17514                 }
17515                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17516                     this.taTask.delay(this.typeAheadDelay);
17517                 }
17518             }
17519         }else{
17520             this.onEmptyResults();
17521         }
17522         
17523         //this.el.focus();
17524     },
17525     // private
17526     onLoadException : function()
17527     {
17528         this.hasQuery = false;
17529         
17530         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17531             this.loading.hide();
17532         }
17533         
17534         if(this.tickable && this.editable){
17535             return;
17536         }
17537         
17538         this.collapse();
17539         // only causes errors at present
17540         //Roo.log(this.store.reader.jsonData);
17541         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17542             // fixme
17543             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17544         //}
17545         
17546         
17547     },
17548     // private
17549     onTypeAhead : function(){
17550         if(this.store.getCount() > 0){
17551             var r = this.store.getAt(0);
17552             var newValue = r.data[this.displayField];
17553             var len = newValue.length;
17554             var selStart = this.getRawValue().length;
17555             
17556             if(selStart != len){
17557                 this.setRawValue(newValue);
17558                 this.selectText(selStart, newValue.length);
17559             }
17560         }
17561     },
17562
17563     // private
17564     onSelect : function(record, index){
17565         
17566         if(this.fireEvent('beforeselect', this, record, index) !== false){
17567         
17568             this.setFromData(index > -1 ? record.data : false);
17569             
17570             this.collapse();
17571             this.fireEvent('select', this, record, index);
17572         }
17573     },
17574
17575     /**
17576      * Returns the currently selected field value or empty string if no value is set.
17577      * @return {String} value The selected value
17578      */
17579     getValue : function()
17580     {
17581         if(Roo.isIOS && this.useNativeIOS){
17582             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17583         }
17584         
17585         if(this.multiple){
17586             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17587         }
17588         
17589         if(this.valueField){
17590             return typeof this.value != 'undefined' ? this.value : '';
17591         }else{
17592             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17593         }
17594     },
17595     
17596     getRawValue : function()
17597     {
17598         if(Roo.isIOS && this.useNativeIOS){
17599             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17600         }
17601         
17602         var v = this.inputEl().getValue();
17603         
17604         return v;
17605     },
17606
17607     /**
17608      * Clears any text/value currently set in the field
17609      */
17610     clearValue : function(){
17611         
17612         if(this.hiddenField){
17613             this.hiddenField.dom.value = '';
17614         }
17615         this.value = '';
17616         this.setRawValue('');
17617         this.lastSelectionText = '';
17618         this.lastData = false;
17619         
17620         var close = this.closeTriggerEl();
17621         
17622         if(close){
17623             close.hide();
17624         }
17625         
17626         this.validate();
17627         
17628     },
17629
17630     /**
17631      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17632      * will be displayed in the field.  If the value does not match the data value of an existing item,
17633      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17634      * Otherwise the field will be blank (although the value will still be set).
17635      * @param {String} value The value to match
17636      */
17637     setValue : function(v)
17638     {
17639         if(Roo.isIOS && this.useNativeIOS){
17640             this.setIOSValue(v);
17641             return;
17642         }
17643         
17644         if(this.multiple){
17645             this.syncValue();
17646             return;
17647         }
17648         
17649         var text = v;
17650         if(this.valueField){
17651             var r = this.findRecord(this.valueField, v);
17652             if(r){
17653                 text = r.data[this.displayField];
17654             }else if(this.valueNotFoundText !== undefined){
17655                 text = this.valueNotFoundText;
17656             }
17657         }
17658         this.lastSelectionText = text;
17659         if(this.hiddenField){
17660             this.hiddenField.dom.value = v;
17661         }
17662         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17663         this.value = v;
17664         
17665         var close = this.closeTriggerEl();
17666         
17667         if(close){
17668             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17669         }
17670         
17671         this.validate();
17672     },
17673     /**
17674      * @property {Object} the last set data for the element
17675      */
17676     
17677     lastData : false,
17678     /**
17679      * Sets the value of the field based on a object which is related to the record format for the store.
17680      * @param {Object} value the value to set as. or false on reset?
17681      */
17682     setFromData : function(o){
17683         
17684         if(this.multiple){
17685             this.addItem(o);
17686             return;
17687         }
17688             
17689         var dv = ''; // display value
17690         var vv = ''; // value value..
17691         this.lastData = o;
17692         if (this.displayField) {
17693             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17694         } else {
17695             // this is an error condition!!!
17696             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17697         }
17698         
17699         if(this.valueField){
17700             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17701         }
17702         
17703         var close = this.closeTriggerEl();
17704         
17705         if(close){
17706             if(dv.length || vv * 1 > 0){
17707                 close.show() ;
17708                 this.blockFocus=true;
17709             } else {
17710                 close.hide();
17711             }             
17712         }
17713         
17714         if(this.hiddenField){
17715             this.hiddenField.dom.value = vv;
17716             
17717             this.lastSelectionText = dv;
17718             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17719             this.value = vv;
17720             return;
17721         }
17722         // no hidden field.. - we store the value in 'value', but still display
17723         // display field!!!!
17724         this.lastSelectionText = dv;
17725         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17726         this.value = vv;
17727         
17728         
17729         
17730     },
17731     // private
17732     reset : function(){
17733         // overridden so that last data is reset..
17734         
17735         if(this.multiple){
17736             this.clearItem();
17737             return;
17738         }
17739         
17740         this.setValue(this.originalValue);
17741         //this.clearInvalid();
17742         this.lastData = false;
17743         if (this.view) {
17744             this.view.clearSelections();
17745         }
17746         
17747         this.validate();
17748     },
17749     // private
17750     findRecord : function(prop, value){
17751         var record;
17752         if(this.store.getCount() > 0){
17753             this.store.each(function(r){
17754                 if(r.data[prop] == value){
17755                     record = r;
17756                     return false;
17757                 }
17758                 return true;
17759             });
17760         }
17761         return record;
17762     },
17763     
17764     getName: function()
17765     {
17766         // returns hidden if it's set..
17767         if (!this.rendered) {return ''};
17768         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17769         
17770     },
17771     // private
17772     onViewMove : function(e, t){
17773         this.inKeyMode = false;
17774     },
17775
17776     // private
17777     onViewOver : function(e, t){
17778         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17779             return;
17780         }
17781         var item = this.view.findItemFromChild(t);
17782         
17783         if(item){
17784             var index = this.view.indexOf(item);
17785             this.select(index, false);
17786         }
17787     },
17788
17789     // private
17790     onViewClick : function(view, doFocus, el, e)
17791     {
17792         var index = this.view.getSelectedIndexes()[0];
17793         
17794         var r = this.store.getAt(index);
17795         
17796         if(this.tickable){
17797             
17798             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17799                 return;
17800             }
17801             
17802             var rm = false;
17803             var _this = this;
17804             
17805             Roo.each(this.tickItems, function(v,k){
17806                 
17807                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17808                     Roo.log(v);
17809                     _this.tickItems.splice(k, 1);
17810                     
17811                     if(typeof(e) == 'undefined' && view == false){
17812                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17813                     }
17814                     
17815                     rm = true;
17816                     return;
17817                 }
17818             });
17819             
17820             if(rm){
17821                 return;
17822             }
17823             
17824             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17825                 this.tickItems.push(r.data);
17826             }
17827             
17828             if(typeof(e) == 'undefined' && view == false){
17829                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17830             }
17831                     
17832             return;
17833         }
17834         
17835         if(r){
17836             this.onSelect(r, index);
17837         }
17838         if(doFocus !== false && !this.blockFocus){
17839             this.inputEl().focus();
17840         }
17841     },
17842
17843     // private
17844     restrictHeight : function(){
17845         //this.innerList.dom.style.height = '';
17846         //var inner = this.innerList.dom;
17847         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17848         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17849         //this.list.beginUpdate();
17850         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17851         this.list.alignTo(this.inputEl(), this.listAlign);
17852         this.list.alignTo(this.inputEl(), this.listAlign);
17853         //this.list.endUpdate();
17854     },
17855
17856     // private
17857     onEmptyResults : function(){
17858         
17859         if(this.tickable && this.editable){
17860             this.hasFocus = false;
17861             this.restrictHeight();
17862             return;
17863         }
17864         
17865         this.collapse();
17866     },
17867
17868     /**
17869      * Returns true if the dropdown list is expanded, else false.
17870      */
17871     isExpanded : function(){
17872         return this.list.isVisible();
17873     },
17874
17875     /**
17876      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17877      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17878      * @param {String} value The data value of the item to select
17879      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17880      * selected item if it is not currently in view (defaults to true)
17881      * @return {Boolean} True if the value matched an item in the list, else false
17882      */
17883     selectByValue : function(v, scrollIntoView){
17884         if(v !== undefined && v !== null){
17885             var r = this.findRecord(this.valueField || this.displayField, v);
17886             if(r){
17887                 this.select(this.store.indexOf(r), scrollIntoView);
17888                 return true;
17889             }
17890         }
17891         return false;
17892     },
17893
17894     /**
17895      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17896      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17897      * @param {Number} index The zero-based index of the list item to select
17898      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17899      * selected item if it is not currently in view (defaults to true)
17900      */
17901     select : function(index, scrollIntoView){
17902         this.selectedIndex = index;
17903         this.view.select(index);
17904         if(scrollIntoView !== false){
17905             var el = this.view.getNode(index);
17906             /*
17907              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17908              */
17909             if(el){
17910                 this.list.scrollChildIntoView(el, false);
17911             }
17912         }
17913     },
17914
17915     // private
17916     selectNext : function(){
17917         var ct = this.store.getCount();
17918         if(ct > 0){
17919             if(this.selectedIndex == -1){
17920                 this.select(0);
17921             }else if(this.selectedIndex < ct-1){
17922                 this.select(this.selectedIndex+1);
17923             }
17924         }
17925     },
17926
17927     // private
17928     selectPrev : function(){
17929         var ct = this.store.getCount();
17930         if(ct > 0){
17931             if(this.selectedIndex == -1){
17932                 this.select(0);
17933             }else if(this.selectedIndex != 0){
17934                 this.select(this.selectedIndex-1);
17935             }
17936         }
17937     },
17938
17939     // private
17940     onKeyUp : function(e){
17941         if(this.editable !== false && !e.isSpecialKey()){
17942             this.lastKey = e.getKey();
17943             this.dqTask.delay(this.queryDelay);
17944         }
17945     },
17946
17947     // private
17948     validateBlur : function(){
17949         return !this.list || !this.list.isVisible();   
17950     },
17951
17952     // private
17953     initQuery : function(){
17954         
17955         var v = this.getRawValue();
17956         
17957         if(this.tickable && this.editable){
17958             v = this.tickableInputEl().getValue();
17959         }
17960         
17961         this.doQuery(v);
17962     },
17963
17964     // private
17965     doForce : function(){
17966         if(this.inputEl().dom.value.length > 0){
17967             this.inputEl().dom.value =
17968                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17969              
17970         }
17971     },
17972
17973     /**
17974      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17975      * query allowing the query action to be canceled if needed.
17976      * @param {String} query The SQL query to execute
17977      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17978      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17979      * saved in the current store (defaults to false)
17980      */
17981     doQuery : function(q, forceAll){
17982         
17983         if(q === undefined || q === null){
17984             q = '';
17985         }
17986         var qe = {
17987             query: q,
17988             forceAll: forceAll,
17989             combo: this,
17990             cancel:false
17991         };
17992         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17993             return false;
17994         }
17995         q = qe.query;
17996         
17997         forceAll = qe.forceAll;
17998         if(forceAll === true || (q.length >= this.minChars)){
17999             
18000             this.hasQuery = true;
18001             
18002             if(this.lastQuery != q || this.alwaysQuery){
18003                 this.lastQuery = q;
18004                 if(this.mode == 'local'){
18005                     this.selectedIndex = -1;
18006                     if(forceAll){
18007                         this.store.clearFilter();
18008                     }else{
18009                         
18010                         if(this.specialFilter){
18011                             this.fireEvent('specialfilter', this);
18012                             this.onLoad();
18013                             return;
18014                         }
18015                         
18016                         this.store.filter(this.displayField, q);
18017                     }
18018                     
18019                     this.store.fireEvent("datachanged", this.store);
18020                     
18021                     this.onLoad();
18022                     
18023                     
18024                 }else{
18025                     
18026                     this.store.baseParams[this.queryParam] = q;
18027                     
18028                     var options = {params : this.getParams(q)};
18029                     
18030                     if(this.loadNext){
18031                         options.add = true;
18032                         options.params.start = this.page * this.pageSize;
18033                     }
18034                     
18035                     this.store.load(options);
18036                     
18037                     /*
18038                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18039                      *  we should expand the list on onLoad
18040                      *  so command out it
18041                      */
18042 //                    this.expand();
18043                 }
18044             }else{
18045                 this.selectedIndex = -1;
18046                 this.onLoad();   
18047             }
18048         }
18049         
18050         this.loadNext = false;
18051     },
18052     
18053     // private
18054     getParams : function(q){
18055         var p = {};
18056         //p[this.queryParam] = q;
18057         
18058         if(this.pageSize){
18059             p.start = 0;
18060             p.limit = this.pageSize;
18061         }
18062         return p;
18063     },
18064
18065     /**
18066      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18067      */
18068     collapse : function(){
18069         if(!this.isExpanded()){
18070             return;
18071         }
18072         
18073         this.list.hide();
18074         
18075         this.hasFocus = false;
18076         
18077         if(this.tickable){
18078             this.okBtn.hide();
18079             this.cancelBtn.hide();
18080             this.trigger.show();
18081             
18082             if(this.editable){
18083                 this.tickableInputEl().dom.value = '';
18084                 this.tickableInputEl().blur();
18085             }
18086             
18087         }
18088         
18089         Roo.get(document).un('mousedown', this.collapseIf, this);
18090         Roo.get(document).un('mousewheel', this.collapseIf, this);
18091         if (!this.editable) {
18092             Roo.get(document).un('keydown', this.listKeyPress, this);
18093         }
18094         this.fireEvent('collapse', this);
18095         
18096         this.validate();
18097     },
18098
18099     // private
18100     collapseIf : function(e){
18101         var in_combo  = e.within(this.el);
18102         var in_list =  e.within(this.list);
18103         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18104         
18105         if (in_combo || in_list || is_list) {
18106             //e.stopPropagation();
18107             return;
18108         }
18109         
18110         if(this.tickable){
18111             this.onTickableFooterButtonClick(e, false, false);
18112         }
18113
18114         this.collapse();
18115         
18116     },
18117
18118     /**
18119      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18120      */
18121     expand : function(){
18122        
18123         if(this.isExpanded() || !this.hasFocus){
18124             return;
18125         }
18126         
18127         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18128         this.list.setWidth(lw);
18129         
18130         Roo.log('expand');
18131         
18132         this.list.show();
18133         
18134         this.restrictHeight();
18135         
18136         if(this.tickable){
18137             
18138             this.tickItems = Roo.apply([], this.item);
18139             
18140             this.okBtn.show();
18141             this.cancelBtn.show();
18142             this.trigger.hide();
18143             
18144             if(this.editable){
18145                 this.tickableInputEl().focus();
18146             }
18147             
18148         }
18149         
18150         Roo.get(document).on('mousedown', this.collapseIf, this);
18151         Roo.get(document).on('mousewheel', this.collapseIf, this);
18152         if (!this.editable) {
18153             Roo.get(document).on('keydown', this.listKeyPress, this);
18154         }
18155         
18156         this.fireEvent('expand', this);
18157     },
18158
18159     // private
18160     // Implements the default empty TriggerField.onTriggerClick function
18161     onTriggerClick : function(e)
18162     {
18163         Roo.log('trigger click');
18164         
18165         if(this.disabled || !this.triggerList){
18166             return;
18167         }
18168         
18169         this.page = 0;
18170         this.loadNext = false;
18171         
18172         if(this.isExpanded()){
18173             this.collapse();
18174             if (!this.blockFocus) {
18175                 this.inputEl().focus();
18176             }
18177             
18178         }else {
18179             this.hasFocus = true;
18180             if(this.triggerAction == 'all') {
18181                 this.doQuery(this.allQuery, true);
18182             } else {
18183                 this.doQuery(this.getRawValue());
18184             }
18185             if (!this.blockFocus) {
18186                 this.inputEl().focus();
18187             }
18188         }
18189     },
18190     
18191     onTickableTriggerClick : function(e)
18192     {
18193         if(this.disabled){
18194             return;
18195         }
18196         
18197         this.page = 0;
18198         this.loadNext = false;
18199         this.hasFocus = true;
18200         
18201         if(this.triggerAction == 'all') {
18202             this.doQuery(this.allQuery, true);
18203         } else {
18204             this.doQuery(this.getRawValue());
18205         }
18206     },
18207     
18208     onSearchFieldClick : function(e)
18209     {
18210         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18211             this.onTickableFooterButtonClick(e, false, false);
18212             return;
18213         }
18214         
18215         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18216             return;
18217         }
18218         
18219         this.page = 0;
18220         this.loadNext = false;
18221         this.hasFocus = true;
18222         
18223         if(this.triggerAction == 'all') {
18224             this.doQuery(this.allQuery, true);
18225         } else {
18226             this.doQuery(this.getRawValue());
18227         }
18228     },
18229     
18230     listKeyPress : function(e)
18231     {
18232         //Roo.log('listkeypress');
18233         // scroll to first matching element based on key pres..
18234         if (e.isSpecialKey()) {
18235             return false;
18236         }
18237         var k = String.fromCharCode(e.getKey()).toUpperCase();
18238         //Roo.log(k);
18239         var match  = false;
18240         var csel = this.view.getSelectedNodes();
18241         var cselitem = false;
18242         if (csel.length) {
18243             var ix = this.view.indexOf(csel[0]);
18244             cselitem  = this.store.getAt(ix);
18245             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18246                 cselitem = false;
18247             }
18248             
18249         }
18250         
18251         this.store.each(function(v) { 
18252             if (cselitem) {
18253                 // start at existing selection.
18254                 if (cselitem.id == v.id) {
18255                     cselitem = false;
18256                 }
18257                 return true;
18258             }
18259                 
18260             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18261                 match = this.store.indexOf(v);
18262                 return false;
18263             }
18264             return true;
18265         }, this);
18266         
18267         if (match === false) {
18268             return true; // no more action?
18269         }
18270         // scroll to?
18271         this.view.select(match);
18272         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18273         sn.scrollIntoView(sn.dom.parentNode, false);
18274     },
18275     
18276     onViewScroll : function(e, t){
18277         
18278         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){
18279             return;
18280         }
18281         
18282         this.hasQuery = true;
18283         
18284         this.loading = this.list.select('.loading', true).first();
18285         
18286         if(this.loading === null){
18287             this.list.createChild({
18288                 tag: 'div',
18289                 cls: 'loading roo-select2-more-results roo-select2-active',
18290                 html: 'Loading more results...'
18291             });
18292             
18293             this.loading = this.list.select('.loading', true).first();
18294             
18295             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18296             
18297             this.loading.hide();
18298         }
18299         
18300         this.loading.show();
18301         
18302         var _combo = this;
18303         
18304         this.page++;
18305         this.loadNext = true;
18306         
18307         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18308         
18309         return;
18310     },
18311     
18312     addItem : function(o)
18313     {   
18314         var dv = ''; // display value
18315         
18316         if (this.displayField) {
18317             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18318         } else {
18319             // this is an error condition!!!
18320             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18321         }
18322         
18323         if(!dv.length){
18324             return;
18325         }
18326         
18327         var choice = this.choices.createChild({
18328             tag: 'li',
18329             cls: 'roo-select2-search-choice',
18330             cn: [
18331                 {
18332                     tag: 'div',
18333                     html: dv
18334                 },
18335                 {
18336                     tag: 'a',
18337                     href: '#',
18338                     cls: 'roo-select2-search-choice-close fa fa-times',
18339                     tabindex: '-1'
18340                 }
18341             ]
18342             
18343         }, this.searchField);
18344         
18345         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18346         
18347         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18348         
18349         this.item.push(o);
18350         
18351         this.lastData = o;
18352         
18353         this.syncValue();
18354         
18355         this.inputEl().dom.value = '';
18356         
18357         this.validate();
18358     },
18359     
18360     onRemoveItem : function(e, _self, o)
18361     {
18362         e.preventDefault();
18363         
18364         this.lastItem = Roo.apply([], this.item);
18365         
18366         var index = this.item.indexOf(o.data) * 1;
18367         
18368         if( index < 0){
18369             Roo.log('not this item?!');
18370             return;
18371         }
18372         
18373         this.item.splice(index, 1);
18374         o.item.remove();
18375         
18376         this.syncValue();
18377         
18378         this.fireEvent('remove', this, e);
18379         
18380         this.validate();
18381         
18382     },
18383     
18384     syncValue : function()
18385     {
18386         if(!this.item.length){
18387             this.clearValue();
18388             return;
18389         }
18390             
18391         var value = [];
18392         var _this = this;
18393         Roo.each(this.item, function(i){
18394             if(_this.valueField){
18395                 value.push(i[_this.valueField]);
18396                 return;
18397             }
18398
18399             value.push(i);
18400         });
18401
18402         this.value = value.join(',');
18403
18404         if(this.hiddenField){
18405             this.hiddenField.dom.value = this.value;
18406         }
18407         
18408         this.store.fireEvent("datachanged", this.store);
18409         
18410         this.validate();
18411     },
18412     
18413     clearItem : function()
18414     {
18415         if(!this.multiple){
18416             return;
18417         }
18418         
18419         this.item = [];
18420         
18421         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18422            c.remove();
18423         });
18424         
18425         this.syncValue();
18426         
18427         this.validate();
18428         
18429         if(this.tickable && !Roo.isTouch){
18430             this.view.refresh();
18431         }
18432     },
18433     
18434     inputEl: function ()
18435     {
18436         if(Roo.isIOS && this.useNativeIOS){
18437             return this.el.select('select.roo-ios-select', true).first();
18438         }
18439         
18440         if(Roo.isTouch && this.mobileTouchView){
18441             return this.el.select('input.form-control',true).first();
18442         }
18443         
18444         if(this.tickable){
18445             return this.searchField;
18446         }
18447         
18448         return this.el.select('input.form-control',true).first();
18449     },
18450     
18451     onTickableFooterButtonClick : function(e, btn, el)
18452     {
18453         e.preventDefault();
18454         
18455         this.lastItem = Roo.apply([], this.item);
18456         
18457         if(btn && btn.name == 'cancel'){
18458             this.tickItems = Roo.apply([], this.item);
18459             this.collapse();
18460             return;
18461         }
18462         
18463         this.clearItem();
18464         
18465         var _this = this;
18466         
18467         Roo.each(this.tickItems, function(o){
18468             _this.addItem(o);
18469         });
18470         
18471         this.collapse();
18472         
18473     },
18474     
18475     validate : function()
18476     {
18477         if(this.getVisibilityEl().hasClass('hidden')){
18478             return true;
18479         }
18480         
18481         var v = this.getRawValue();
18482         
18483         if(this.multiple){
18484             v = this.getValue();
18485         }
18486         
18487         if(this.disabled || this.allowBlank || v.length){
18488             this.markValid();
18489             return true;
18490         }
18491         
18492         this.markInvalid();
18493         return false;
18494     },
18495     
18496     tickableInputEl : function()
18497     {
18498         if(!this.tickable || !this.editable){
18499             return this.inputEl();
18500         }
18501         
18502         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18503     },
18504     
18505     
18506     getAutoCreateTouchView : function()
18507     {
18508         var id = Roo.id();
18509         
18510         var cfg = {
18511             cls: 'form-group' //input-group
18512         };
18513         
18514         var input =  {
18515             tag: 'input',
18516             id : id,
18517             type : this.inputType,
18518             cls : 'form-control x-combo-noedit',
18519             autocomplete: 'new-password',
18520             placeholder : this.placeholder || '',
18521             readonly : true
18522         };
18523         
18524         if (this.name) {
18525             input.name = this.name;
18526         }
18527         
18528         if (this.size) {
18529             input.cls += ' input-' + this.size;
18530         }
18531         
18532         if (this.disabled) {
18533             input.disabled = true;
18534         }
18535         
18536         var inputblock = {
18537             cls : 'roo-combobox-wrap',
18538             cn : [
18539                 input
18540             ]
18541         };
18542         
18543         if(this.before){
18544             inputblock.cls += ' input-group';
18545             
18546             inputblock.cn.unshift({
18547                 tag :'span',
18548                 cls : 'input-group-addon input-group-prepend input-group-text',
18549                 html : this.before
18550             });
18551         }
18552         
18553         if(this.removable && !this.multiple){
18554             inputblock.cls += ' roo-removable';
18555             
18556             inputblock.cn.push({
18557                 tag: 'button',
18558                 html : 'x',
18559                 cls : 'roo-combo-removable-btn close'
18560             });
18561         }
18562
18563         if(this.hasFeedback && !this.allowBlank){
18564             
18565             inputblock.cls += ' has-feedback';
18566             
18567             inputblock.cn.push({
18568                 tag: 'span',
18569                 cls: 'glyphicon form-control-feedback'
18570             });
18571             
18572         }
18573         
18574         if (this.after) {
18575             
18576             inputblock.cls += (this.before) ? '' : ' input-group';
18577             
18578             inputblock.cn.push({
18579                 tag :'span',
18580                 cls : 'input-group-addon input-group-append input-group-text',
18581                 html : this.after
18582             });
18583         }
18584
18585         
18586         var ibwrap = inputblock;
18587         
18588         if(this.multiple){
18589             ibwrap = {
18590                 tag: 'ul',
18591                 cls: 'roo-select2-choices',
18592                 cn:[
18593                     {
18594                         tag: 'li',
18595                         cls: 'roo-select2-search-field',
18596                         cn: [
18597
18598                             inputblock
18599                         ]
18600                     }
18601                 ]
18602             };
18603         
18604             
18605         }
18606         
18607         var combobox = {
18608             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18609             cn: [
18610                 {
18611                     tag: 'input',
18612                     type : 'hidden',
18613                     cls: 'form-hidden-field'
18614                 },
18615                 ibwrap
18616             ]
18617         };
18618         
18619         if(!this.multiple && this.showToggleBtn){
18620             
18621             var caret = {
18622                 cls: 'caret'
18623             };
18624             
18625             if (this.caret != false) {
18626                 caret = {
18627                      tag: 'i',
18628                      cls: 'fa fa-' + this.caret
18629                 };
18630                 
18631             }
18632             
18633             combobox.cn.push({
18634                 tag :'span',
18635                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18636                 cn : [
18637                     Roo.bootstrap.version == 3 ? caret : '',
18638                     {
18639                         tag: 'span',
18640                         cls: 'combobox-clear',
18641                         cn  : [
18642                             {
18643                                 tag : 'i',
18644                                 cls: 'icon-remove'
18645                             }
18646                         ]
18647                     }
18648                 ]
18649
18650             })
18651         }
18652         
18653         if(this.multiple){
18654             combobox.cls += ' roo-select2-container-multi';
18655         }
18656         
18657         var required =  this.allowBlank ?  {
18658                     tag : 'i',
18659                     style: 'display: none'
18660                 } : {
18661                    tag : 'i',
18662                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18663                    tooltip : 'This field is required'
18664                 };
18665         
18666         var align = this.labelAlign || this.parentLabelAlign();
18667         
18668         if (align ==='left' && this.fieldLabel.length) {
18669
18670             cfg.cn = [
18671                 required,
18672                 {
18673                     tag: 'label',
18674                     cls : 'control-label col-form-label',
18675                     html : this.fieldLabel
18676
18677                 },
18678                 {
18679                     cls : 'roo-combobox-wrap ', 
18680                     cn: [
18681                         combobox
18682                     ]
18683                 }
18684             ];
18685             
18686             var labelCfg = cfg.cn[1];
18687             var contentCfg = cfg.cn[2];
18688             
18689
18690             if(this.indicatorpos == 'right'){
18691                 cfg.cn = [
18692                     {
18693                         tag: 'label',
18694                         'for' :  id,
18695                         cls : 'control-label col-form-label',
18696                         cn : [
18697                             {
18698                                 tag : 'span',
18699                                 html : this.fieldLabel
18700                             },
18701                             required
18702                         ]
18703                     },
18704                     {
18705                         cls : "roo-combobox-wrap ",
18706                         cn: [
18707                             combobox
18708                         ]
18709                     }
18710
18711                 ];
18712                 
18713                 labelCfg = cfg.cn[0];
18714                 contentCfg = cfg.cn[1];
18715             }
18716             
18717            
18718             
18719             if(this.labelWidth > 12){
18720                 labelCfg.style = "width: " + this.labelWidth + 'px';
18721             }
18722            
18723             if(this.labelWidth < 13 && this.labelmd == 0){
18724                 this.labelmd = this.labelWidth;
18725             }
18726             
18727             if(this.labellg > 0){
18728                 labelCfg.cls += ' col-lg-' + this.labellg;
18729                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18730             }
18731             
18732             if(this.labelmd > 0){
18733                 labelCfg.cls += ' col-md-' + this.labelmd;
18734                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18735             }
18736             
18737             if(this.labelsm > 0){
18738                 labelCfg.cls += ' col-sm-' + this.labelsm;
18739                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18740             }
18741             
18742             if(this.labelxs > 0){
18743                 labelCfg.cls += ' col-xs-' + this.labelxs;
18744                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18745             }
18746                 
18747                 
18748         } else if ( this.fieldLabel.length) {
18749             cfg.cn = [
18750                required,
18751                 {
18752                     tag: 'label',
18753                     cls : 'control-label',
18754                     html : this.fieldLabel
18755
18756                 },
18757                 {
18758                     cls : '', 
18759                     cn: [
18760                         combobox
18761                     ]
18762                 }
18763             ];
18764             
18765             if(this.indicatorpos == 'right'){
18766                 cfg.cn = [
18767                     {
18768                         tag: 'label',
18769                         cls : 'control-label',
18770                         html : this.fieldLabel,
18771                         cn : [
18772                             required
18773                         ]
18774                     },
18775                     {
18776                         cls : '', 
18777                         cn: [
18778                             combobox
18779                         ]
18780                     }
18781                 ];
18782             }
18783         } else {
18784             cfg.cn = combobox;    
18785         }
18786         
18787         
18788         var settings = this;
18789         
18790         ['xs','sm','md','lg'].map(function(size){
18791             if (settings[size]) {
18792                 cfg.cls += ' col-' + size + '-' + settings[size];
18793             }
18794         });
18795         
18796         return cfg;
18797     },
18798     
18799     initTouchView : function()
18800     {
18801         this.renderTouchView();
18802         
18803         this.touchViewEl.on('scroll', function(){
18804             this.el.dom.scrollTop = 0;
18805         }, this);
18806         
18807         this.originalValue = this.getValue();
18808         
18809         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18810         
18811         this.inputEl().on("click", this.showTouchView, this);
18812         if (this.triggerEl) {
18813             this.triggerEl.on("click", this.showTouchView, this);
18814         }
18815         
18816         
18817         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18818         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18819         
18820         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18821         
18822         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18823         this.store.on('load', this.onTouchViewLoad, this);
18824         this.store.on('loadexception', this.onTouchViewLoadException, this);
18825         
18826         if(this.hiddenName){
18827             
18828             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18829             
18830             this.hiddenField.dom.value =
18831                 this.hiddenValue !== undefined ? this.hiddenValue :
18832                 this.value !== undefined ? this.value : '';
18833         
18834             this.el.dom.removeAttribute('name');
18835             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18836         }
18837         
18838         if(this.multiple){
18839             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18840             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18841         }
18842         
18843         if(this.removable && !this.multiple){
18844             var close = this.closeTriggerEl();
18845             if(close){
18846                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18847                 close.on('click', this.removeBtnClick, this, close);
18848             }
18849         }
18850         /*
18851          * fix the bug in Safari iOS8
18852          */
18853         this.inputEl().on("focus", function(e){
18854             document.activeElement.blur();
18855         }, this);
18856         
18857         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18858         
18859         return;
18860         
18861         
18862     },
18863     
18864     renderTouchView : function()
18865     {
18866         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18867         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18868         
18869         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18870         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18871         
18872         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18873         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18874         this.touchViewBodyEl.setStyle('overflow', 'auto');
18875         
18876         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18877         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18878         
18879         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18880         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18881         
18882     },
18883     
18884     showTouchView : function()
18885     {
18886         if(this.disabled){
18887             return;
18888         }
18889         
18890         this.touchViewHeaderEl.hide();
18891
18892         if(this.modalTitle.length){
18893             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18894             this.touchViewHeaderEl.show();
18895         }
18896
18897         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18898         this.touchViewEl.show();
18899
18900         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18901         
18902         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18903         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18904
18905         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18906
18907         if(this.modalTitle.length){
18908             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18909         }
18910         
18911         this.touchViewBodyEl.setHeight(bodyHeight);
18912
18913         if(this.animate){
18914             var _this = this;
18915             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18916         }else{
18917             this.touchViewEl.addClass(['in','show']);
18918         }
18919         
18920         if(this._touchViewMask){
18921             Roo.get(document.body).addClass("x-body-masked");
18922             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18923             this._touchViewMask.setStyle('z-index', 10000);
18924             this._touchViewMask.addClass('show');
18925         }
18926         
18927         this.doTouchViewQuery();
18928         
18929     },
18930     
18931     hideTouchView : function()
18932     {
18933         this.touchViewEl.removeClass(['in','show']);
18934
18935         if(this.animate){
18936             var _this = this;
18937             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18938         }else{
18939             this.touchViewEl.setStyle('display', 'none');
18940         }
18941         
18942         if(this._touchViewMask){
18943             this._touchViewMask.removeClass('show');
18944             Roo.get(document.body).removeClass("x-body-masked");
18945         }
18946     },
18947     
18948     setTouchViewValue : function()
18949     {
18950         if(this.multiple){
18951             this.clearItem();
18952         
18953             var _this = this;
18954
18955             Roo.each(this.tickItems, function(o){
18956                 this.addItem(o);
18957             }, this);
18958         }
18959         
18960         this.hideTouchView();
18961     },
18962     
18963     doTouchViewQuery : function()
18964     {
18965         var qe = {
18966             query: '',
18967             forceAll: true,
18968             combo: this,
18969             cancel:false
18970         };
18971         
18972         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18973             return false;
18974         }
18975         
18976         if(!this.alwaysQuery || this.mode == 'local'){
18977             this.onTouchViewLoad();
18978             return;
18979         }
18980         
18981         this.store.load();
18982     },
18983     
18984     onTouchViewBeforeLoad : function(combo,opts)
18985     {
18986         return;
18987     },
18988
18989     // private
18990     onTouchViewLoad : function()
18991     {
18992         if(this.store.getCount() < 1){
18993             this.onTouchViewEmptyResults();
18994             return;
18995         }
18996         
18997         this.clearTouchView();
18998         
18999         var rawValue = this.getRawValue();
19000         
19001         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19002         
19003         this.tickItems = [];
19004         
19005         this.store.data.each(function(d, rowIndex){
19006             var row = this.touchViewListGroup.createChild(template);
19007             
19008             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19009                 row.addClass(d.data.cls);
19010             }
19011             
19012             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19013                 var cfg = {
19014                     data : d.data,
19015                     html : d.data[this.displayField]
19016                 };
19017                 
19018                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19019                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19020                 }
19021             }
19022             row.removeClass('selected');
19023             if(!this.multiple && this.valueField &&
19024                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19025             {
19026                 // radio buttons..
19027                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19028                 row.addClass('selected');
19029             }
19030             
19031             if(this.multiple && this.valueField &&
19032                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19033             {
19034                 
19035                 // checkboxes...
19036                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19037                 this.tickItems.push(d.data);
19038             }
19039             
19040             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19041             
19042         }, this);
19043         
19044         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19045         
19046         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19047
19048         if(this.modalTitle.length){
19049             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19050         }
19051
19052         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19053         
19054         if(this.mobile_restrict_height && listHeight < bodyHeight){
19055             this.touchViewBodyEl.setHeight(listHeight);
19056         }
19057         
19058         var _this = this;
19059         
19060         if(firstChecked && listHeight > bodyHeight){
19061             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19062         }
19063         
19064     },
19065     
19066     onTouchViewLoadException : function()
19067     {
19068         this.hideTouchView();
19069     },
19070     
19071     onTouchViewEmptyResults : function()
19072     {
19073         this.clearTouchView();
19074         
19075         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19076         
19077         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19078         
19079     },
19080     
19081     clearTouchView : function()
19082     {
19083         this.touchViewListGroup.dom.innerHTML = '';
19084     },
19085     
19086     onTouchViewClick : function(e, el, o)
19087     {
19088         e.preventDefault();
19089         
19090         var row = o.row;
19091         var rowIndex = o.rowIndex;
19092         
19093         var r = this.store.getAt(rowIndex);
19094         
19095         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19096             
19097             if(!this.multiple){
19098                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19099                     c.dom.removeAttribute('checked');
19100                 }, this);
19101
19102                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19103
19104                 this.setFromData(r.data);
19105
19106                 var close = this.closeTriggerEl();
19107
19108                 if(close){
19109                     close.show();
19110                 }
19111
19112                 this.hideTouchView();
19113
19114                 this.fireEvent('select', this, r, rowIndex);
19115
19116                 return;
19117             }
19118
19119             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19120                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19121                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19122                 return;
19123             }
19124
19125             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19126             this.addItem(r.data);
19127             this.tickItems.push(r.data);
19128         }
19129     },
19130     
19131     getAutoCreateNativeIOS : function()
19132     {
19133         var cfg = {
19134             cls: 'form-group' //input-group,
19135         };
19136         
19137         var combobox =  {
19138             tag: 'select',
19139             cls : 'roo-ios-select'
19140         };
19141         
19142         if (this.name) {
19143             combobox.name = this.name;
19144         }
19145         
19146         if (this.disabled) {
19147             combobox.disabled = true;
19148         }
19149         
19150         var settings = this;
19151         
19152         ['xs','sm','md','lg'].map(function(size){
19153             if (settings[size]) {
19154                 cfg.cls += ' col-' + size + '-' + settings[size];
19155             }
19156         });
19157         
19158         cfg.cn = combobox;
19159         
19160         return cfg;
19161         
19162     },
19163     
19164     initIOSView : function()
19165     {
19166         this.store.on('load', this.onIOSViewLoad, this);
19167         
19168         return;
19169     },
19170     
19171     onIOSViewLoad : function()
19172     {
19173         if(this.store.getCount() < 1){
19174             return;
19175         }
19176         
19177         this.clearIOSView();
19178         
19179         if(this.allowBlank) {
19180             
19181             var default_text = '-- SELECT --';
19182             
19183             if(this.placeholder.length){
19184                 default_text = this.placeholder;
19185             }
19186             
19187             if(this.emptyTitle.length){
19188                 default_text += ' - ' + this.emptyTitle + ' -';
19189             }
19190             
19191             var opt = this.inputEl().createChild({
19192                 tag: 'option',
19193                 value : 0,
19194                 html : default_text
19195             });
19196             
19197             var o = {};
19198             o[this.valueField] = 0;
19199             o[this.displayField] = default_text;
19200             
19201             this.ios_options.push({
19202                 data : o,
19203                 el : opt
19204             });
19205             
19206         }
19207         
19208         this.store.data.each(function(d, rowIndex){
19209             
19210             var html = '';
19211             
19212             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19213                 html = d.data[this.displayField];
19214             }
19215             
19216             var value = '';
19217             
19218             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19219                 value = d.data[this.valueField];
19220             }
19221             
19222             var option = {
19223                 tag: 'option',
19224                 value : value,
19225                 html : html
19226             };
19227             
19228             if(this.value == d.data[this.valueField]){
19229                 option['selected'] = true;
19230             }
19231             
19232             var opt = this.inputEl().createChild(option);
19233             
19234             this.ios_options.push({
19235                 data : d.data,
19236                 el : opt
19237             });
19238             
19239         }, this);
19240         
19241         this.inputEl().on('change', function(){
19242            this.fireEvent('select', this);
19243         }, this);
19244         
19245     },
19246     
19247     clearIOSView: function()
19248     {
19249         this.inputEl().dom.innerHTML = '';
19250         
19251         this.ios_options = [];
19252     },
19253     
19254     setIOSValue: function(v)
19255     {
19256         this.value = v;
19257         
19258         if(!this.ios_options){
19259             return;
19260         }
19261         
19262         Roo.each(this.ios_options, function(opts){
19263            
19264            opts.el.dom.removeAttribute('selected');
19265            
19266            if(opts.data[this.valueField] != v){
19267                return;
19268            }
19269            
19270            opts.el.dom.setAttribute('selected', true);
19271            
19272         }, this);
19273     }
19274
19275     /** 
19276     * @cfg {Boolean} grow 
19277     * @hide 
19278     */
19279     /** 
19280     * @cfg {Number} growMin 
19281     * @hide 
19282     */
19283     /** 
19284     * @cfg {Number} growMax 
19285     * @hide 
19286     */
19287     /**
19288      * @hide
19289      * @method autoSize
19290      */
19291 });
19292
19293 Roo.apply(Roo.bootstrap.ComboBox,  {
19294     
19295     header : {
19296         tag: 'div',
19297         cls: 'modal-header',
19298         cn: [
19299             {
19300                 tag: 'h4',
19301                 cls: 'modal-title'
19302             }
19303         ]
19304     },
19305     
19306     body : {
19307         tag: 'div',
19308         cls: 'modal-body',
19309         cn: [
19310             {
19311                 tag: 'ul',
19312                 cls: 'list-group'
19313             }
19314         ]
19315     },
19316     
19317     listItemRadio : {
19318         tag: 'li',
19319         cls: 'list-group-item',
19320         cn: [
19321             {
19322                 tag: 'span',
19323                 cls: 'roo-combobox-list-group-item-value'
19324             },
19325             {
19326                 tag: 'div',
19327                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19328                 cn: [
19329                     {
19330                         tag: 'input',
19331                         type: 'radio'
19332                     },
19333                     {
19334                         tag: 'label'
19335                     }
19336                 ]
19337             }
19338         ]
19339     },
19340     
19341     listItemCheckbox : {
19342         tag: 'li',
19343         cls: 'list-group-item',
19344         cn: [
19345             {
19346                 tag: 'span',
19347                 cls: 'roo-combobox-list-group-item-value'
19348             },
19349             {
19350                 tag: 'div',
19351                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19352                 cn: [
19353                     {
19354                         tag: 'input',
19355                         type: 'checkbox'
19356                     },
19357                     {
19358                         tag: 'label'
19359                     }
19360                 ]
19361             }
19362         ]
19363     },
19364     
19365     emptyResult : {
19366         tag: 'div',
19367         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19368     },
19369     
19370     footer : {
19371         tag: 'div',
19372         cls: 'modal-footer',
19373         cn: [
19374             {
19375                 tag: 'div',
19376                 cls: 'row',
19377                 cn: [
19378                     {
19379                         tag: 'div',
19380                         cls: 'col-xs-6 text-left',
19381                         cn: {
19382                             tag: 'button',
19383                             cls: 'btn btn-danger roo-touch-view-cancel',
19384                             html: 'Cancel'
19385                         }
19386                     },
19387                     {
19388                         tag: 'div',
19389                         cls: 'col-xs-6 text-right',
19390                         cn: {
19391                             tag: 'button',
19392                             cls: 'btn btn-success roo-touch-view-ok',
19393                             html: 'OK'
19394                         }
19395                     }
19396                 ]
19397             }
19398         ]
19399         
19400     }
19401 });
19402
19403 Roo.apply(Roo.bootstrap.ComboBox,  {
19404     
19405     touchViewTemplate : {
19406         tag: 'div',
19407         cls: 'modal fade roo-combobox-touch-view',
19408         cn: [
19409             {
19410                 tag: 'div',
19411                 cls: 'modal-dialog',
19412                 style : 'position:fixed', // we have to fix position....
19413                 cn: [
19414                     {
19415                         tag: 'div',
19416                         cls: 'modal-content',
19417                         cn: [
19418                             Roo.bootstrap.ComboBox.header,
19419                             Roo.bootstrap.ComboBox.body,
19420                             Roo.bootstrap.ComboBox.footer
19421                         ]
19422                     }
19423                 ]
19424             }
19425         ]
19426     }
19427 });/*
19428  * Based on:
19429  * Ext JS Library 1.1.1
19430  * Copyright(c) 2006-2007, Ext JS, LLC.
19431  *
19432  * Originally Released Under LGPL - original licence link has changed is not relivant.
19433  *
19434  * Fork - LGPL
19435  * <script type="text/javascript">
19436  */
19437
19438 /**
19439  * @class Roo.View
19440  * @extends Roo.util.Observable
19441  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19442  * This class also supports single and multi selection modes. <br>
19443  * Create a data model bound view:
19444  <pre><code>
19445  var store = new Roo.data.Store(...);
19446
19447  var view = new Roo.View({
19448     el : "my-element",
19449     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19450  
19451     singleSelect: true,
19452     selectedClass: "ydataview-selected",
19453     store: store
19454  });
19455
19456  // listen for node click?
19457  view.on("click", function(vw, index, node, e){
19458  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19459  });
19460
19461  // load XML data
19462  dataModel.load("foobar.xml");
19463  </code></pre>
19464  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19465  * <br><br>
19466  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19467  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19468  * 
19469  * Note: old style constructor is still suported (container, template, config)
19470  * 
19471  * @constructor
19472  * Create a new View
19473  * @param {Object} config The config object
19474  * 
19475  */
19476 Roo.View = function(config, depreciated_tpl, depreciated_config){
19477     
19478     this.parent = false;
19479     
19480     if (typeof(depreciated_tpl) == 'undefined') {
19481         // new way.. - universal constructor.
19482         Roo.apply(this, config);
19483         this.el  = Roo.get(this.el);
19484     } else {
19485         // old format..
19486         this.el  = Roo.get(config);
19487         this.tpl = depreciated_tpl;
19488         Roo.apply(this, depreciated_config);
19489     }
19490     this.wrapEl  = this.el.wrap().wrap();
19491     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19492     
19493     
19494     if(typeof(this.tpl) == "string"){
19495         this.tpl = new Roo.Template(this.tpl);
19496     } else {
19497         // support xtype ctors..
19498         this.tpl = new Roo.factory(this.tpl, Roo);
19499     }
19500     
19501     
19502     this.tpl.compile();
19503     
19504     /** @private */
19505     this.addEvents({
19506         /**
19507          * @event beforeclick
19508          * Fires before a click is processed. Returns false to cancel the default action.
19509          * @param {Roo.View} this
19510          * @param {Number} index The index of the target node
19511          * @param {HTMLElement} node The target node
19512          * @param {Roo.EventObject} e The raw event object
19513          */
19514             "beforeclick" : true,
19515         /**
19516          * @event click
19517          * Fires when a template node is clicked.
19518          * @param {Roo.View} this
19519          * @param {Number} index The index of the target node
19520          * @param {HTMLElement} node The target node
19521          * @param {Roo.EventObject} e The raw event object
19522          */
19523             "click" : true,
19524         /**
19525          * @event dblclick
19526          * Fires when a template node is double clicked.
19527          * @param {Roo.View} this
19528          * @param {Number} index The index of the target node
19529          * @param {HTMLElement} node The target node
19530          * @param {Roo.EventObject} e The raw event object
19531          */
19532             "dblclick" : true,
19533         /**
19534          * @event contextmenu
19535          * Fires when a template node is right clicked.
19536          * @param {Roo.View} this
19537          * @param {Number} index The index of the target node
19538          * @param {HTMLElement} node The target node
19539          * @param {Roo.EventObject} e The raw event object
19540          */
19541             "contextmenu" : true,
19542         /**
19543          * @event selectionchange
19544          * Fires when the selected nodes change.
19545          * @param {Roo.View} this
19546          * @param {Array} selections Array of the selected nodes
19547          */
19548             "selectionchange" : true,
19549     
19550         /**
19551          * @event beforeselect
19552          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19553          * @param {Roo.View} this
19554          * @param {HTMLElement} node The node to be selected
19555          * @param {Array} selections Array of currently selected nodes
19556          */
19557             "beforeselect" : true,
19558         /**
19559          * @event preparedata
19560          * Fires on every row to render, to allow you to change the data.
19561          * @param {Roo.View} this
19562          * @param {Object} data to be rendered (change this)
19563          */
19564           "preparedata" : true
19565           
19566           
19567         });
19568
19569
19570
19571     this.el.on({
19572         "click": this.onClick,
19573         "dblclick": this.onDblClick,
19574         "contextmenu": this.onContextMenu,
19575         scope:this
19576     });
19577
19578     this.selections = [];
19579     this.nodes = [];
19580     this.cmp = new Roo.CompositeElementLite([]);
19581     if(this.store){
19582         this.store = Roo.factory(this.store, Roo.data);
19583         this.setStore(this.store, true);
19584     }
19585     
19586     if ( this.footer && this.footer.xtype) {
19587            
19588          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19589         
19590         this.footer.dataSource = this.store;
19591         this.footer.container = fctr;
19592         this.footer = Roo.factory(this.footer, Roo);
19593         fctr.insertFirst(this.el);
19594         
19595         // this is a bit insane - as the paging toolbar seems to detach the el..
19596 //        dom.parentNode.parentNode.parentNode
19597          // they get detached?
19598     }
19599     
19600     
19601     Roo.View.superclass.constructor.call(this);
19602     
19603     
19604 };
19605
19606 Roo.extend(Roo.View, Roo.util.Observable, {
19607     
19608      /**
19609      * @cfg {Roo.data.Store} store Data store to load data from.
19610      */
19611     store : false,
19612     
19613     /**
19614      * @cfg {String|Roo.Element} el The container element.
19615      */
19616     el : '',
19617     
19618     /**
19619      * @cfg {String|Roo.Template} tpl The template used by this View 
19620      */
19621     tpl : false,
19622     /**
19623      * @cfg {String} dataName the named area of the template to use as the data area
19624      *                          Works with domtemplates roo-name="name"
19625      */
19626     dataName: false,
19627     /**
19628      * @cfg {String} selectedClass The css class to add to selected nodes
19629      */
19630     selectedClass : "x-view-selected",
19631      /**
19632      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19633      */
19634     emptyText : "",
19635     
19636     /**
19637      * @cfg {String} text to display on mask (default Loading)
19638      */
19639     mask : false,
19640     /**
19641      * @cfg {Boolean} multiSelect Allow multiple selection
19642      */
19643     multiSelect : false,
19644     /**
19645      * @cfg {Boolean} singleSelect Allow single selection
19646      */
19647     singleSelect:  false,
19648     
19649     /**
19650      * @cfg {Boolean} toggleSelect - selecting 
19651      */
19652     toggleSelect : false,
19653     
19654     /**
19655      * @cfg {Boolean} tickable - selecting 
19656      */
19657     tickable : false,
19658     
19659     /**
19660      * Returns the element this view is bound to.
19661      * @return {Roo.Element}
19662      */
19663     getEl : function(){
19664         return this.wrapEl;
19665     },
19666     
19667     
19668
19669     /**
19670      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19671      */
19672     refresh : function(){
19673         //Roo.log('refresh');
19674         var t = this.tpl;
19675         
19676         // if we are using something like 'domtemplate', then
19677         // the what gets used is:
19678         // t.applySubtemplate(NAME, data, wrapping data..)
19679         // the outer template then get' applied with
19680         //     the store 'extra data'
19681         // and the body get's added to the
19682         //      roo-name="data" node?
19683         //      <span class='roo-tpl-{name}'></span> ?????
19684         
19685         
19686         
19687         this.clearSelections();
19688         this.el.update("");
19689         var html = [];
19690         var records = this.store.getRange();
19691         if(records.length < 1) {
19692             
19693             // is this valid??  = should it render a template??
19694             
19695             this.el.update(this.emptyText);
19696             return;
19697         }
19698         var el = this.el;
19699         if (this.dataName) {
19700             this.el.update(t.apply(this.store.meta)); //????
19701             el = this.el.child('.roo-tpl-' + this.dataName);
19702         }
19703         
19704         for(var i = 0, len = records.length; i < len; i++){
19705             var data = this.prepareData(records[i].data, i, records[i]);
19706             this.fireEvent("preparedata", this, data, i, records[i]);
19707             
19708             var d = Roo.apply({}, data);
19709             
19710             if(this.tickable){
19711                 Roo.apply(d, {'roo-id' : Roo.id()});
19712                 
19713                 var _this = this;
19714             
19715                 Roo.each(this.parent.item, function(item){
19716                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19717                         return;
19718                     }
19719                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19720                 });
19721             }
19722             
19723             html[html.length] = Roo.util.Format.trim(
19724                 this.dataName ?
19725                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19726                     t.apply(d)
19727             );
19728         }
19729         
19730         
19731         
19732         el.update(html.join(""));
19733         this.nodes = el.dom.childNodes;
19734         this.updateIndexes(0);
19735     },
19736     
19737
19738     /**
19739      * Function to override to reformat the data that is sent to
19740      * the template for each node.
19741      * DEPRICATED - use the preparedata event handler.
19742      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19743      * a JSON object for an UpdateManager bound view).
19744      */
19745     prepareData : function(data, index, record)
19746     {
19747         this.fireEvent("preparedata", this, data, index, record);
19748         return data;
19749     },
19750
19751     onUpdate : function(ds, record){
19752         // Roo.log('on update');   
19753         this.clearSelections();
19754         var index = this.store.indexOf(record);
19755         var n = this.nodes[index];
19756         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19757         n.parentNode.removeChild(n);
19758         this.updateIndexes(index, index);
19759     },
19760
19761     
19762     
19763 // --------- FIXME     
19764     onAdd : function(ds, records, index)
19765     {
19766         //Roo.log(['on Add', ds, records, index] );        
19767         this.clearSelections();
19768         if(this.nodes.length == 0){
19769             this.refresh();
19770             return;
19771         }
19772         var n = this.nodes[index];
19773         for(var i = 0, len = records.length; i < len; i++){
19774             var d = this.prepareData(records[i].data, i, records[i]);
19775             if(n){
19776                 this.tpl.insertBefore(n, d);
19777             }else{
19778                 
19779                 this.tpl.append(this.el, d);
19780             }
19781         }
19782         this.updateIndexes(index);
19783     },
19784
19785     onRemove : function(ds, record, index){
19786        // Roo.log('onRemove');
19787         this.clearSelections();
19788         var el = this.dataName  ?
19789             this.el.child('.roo-tpl-' + this.dataName) :
19790             this.el; 
19791         
19792         el.dom.removeChild(this.nodes[index]);
19793         this.updateIndexes(index);
19794     },
19795
19796     /**
19797      * Refresh an individual node.
19798      * @param {Number} index
19799      */
19800     refreshNode : function(index){
19801         this.onUpdate(this.store, this.store.getAt(index));
19802     },
19803
19804     updateIndexes : function(startIndex, endIndex){
19805         var ns = this.nodes;
19806         startIndex = startIndex || 0;
19807         endIndex = endIndex || ns.length - 1;
19808         for(var i = startIndex; i <= endIndex; i++){
19809             ns[i].nodeIndex = i;
19810         }
19811     },
19812
19813     /**
19814      * Changes the data store this view uses and refresh the view.
19815      * @param {Store} store
19816      */
19817     setStore : function(store, initial){
19818         if(!initial && this.store){
19819             this.store.un("datachanged", this.refresh);
19820             this.store.un("add", this.onAdd);
19821             this.store.un("remove", this.onRemove);
19822             this.store.un("update", this.onUpdate);
19823             this.store.un("clear", this.refresh);
19824             this.store.un("beforeload", this.onBeforeLoad);
19825             this.store.un("load", this.onLoad);
19826             this.store.un("loadexception", this.onLoad);
19827         }
19828         if(store){
19829           
19830             store.on("datachanged", this.refresh, this);
19831             store.on("add", this.onAdd, this);
19832             store.on("remove", this.onRemove, this);
19833             store.on("update", this.onUpdate, this);
19834             store.on("clear", this.refresh, this);
19835             store.on("beforeload", this.onBeforeLoad, this);
19836             store.on("load", this.onLoad, this);
19837             store.on("loadexception", this.onLoad, this);
19838         }
19839         
19840         if(store){
19841             this.refresh();
19842         }
19843     },
19844     /**
19845      * onbeforeLoad - masks the loading area.
19846      *
19847      */
19848     onBeforeLoad : function(store,opts)
19849     {
19850          //Roo.log('onBeforeLoad');   
19851         if (!opts.add) {
19852             this.el.update("");
19853         }
19854         this.el.mask(this.mask ? this.mask : "Loading" ); 
19855     },
19856     onLoad : function ()
19857     {
19858         this.el.unmask();
19859     },
19860     
19861
19862     /**
19863      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19864      * @param {HTMLElement} node
19865      * @return {HTMLElement} The template node
19866      */
19867     findItemFromChild : function(node){
19868         var el = this.dataName  ?
19869             this.el.child('.roo-tpl-' + this.dataName,true) :
19870             this.el.dom; 
19871         
19872         if(!node || node.parentNode == el){
19873                     return node;
19874             }
19875             var p = node.parentNode;
19876             while(p && p != el){
19877             if(p.parentNode == el){
19878                 return p;
19879             }
19880             p = p.parentNode;
19881         }
19882             return null;
19883     },
19884
19885     /** @ignore */
19886     onClick : function(e){
19887         var item = this.findItemFromChild(e.getTarget());
19888         if(item){
19889             var index = this.indexOf(item);
19890             if(this.onItemClick(item, index, e) !== false){
19891                 this.fireEvent("click", this, index, item, e);
19892             }
19893         }else{
19894             this.clearSelections();
19895         }
19896     },
19897
19898     /** @ignore */
19899     onContextMenu : function(e){
19900         var item = this.findItemFromChild(e.getTarget());
19901         if(item){
19902             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19903         }
19904     },
19905
19906     /** @ignore */
19907     onDblClick : function(e){
19908         var item = this.findItemFromChild(e.getTarget());
19909         if(item){
19910             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19911         }
19912     },
19913
19914     onItemClick : function(item, index, e)
19915     {
19916         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19917             return false;
19918         }
19919         if (this.toggleSelect) {
19920             var m = this.isSelected(item) ? 'unselect' : 'select';
19921             //Roo.log(m);
19922             var _t = this;
19923             _t[m](item, true, false);
19924             return true;
19925         }
19926         if(this.multiSelect || this.singleSelect){
19927             if(this.multiSelect && e.shiftKey && this.lastSelection){
19928                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19929             }else{
19930                 this.select(item, this.multiSelect && e.ctrlKey);
19931                 this.lastSelection = item;
19932             }
19933             
19934             if(!this.tickable){
19935                 e.preventDefault();
19936             }
19937             
19938         }
19939         return true;
19940     },
19941
19942     /**
19943      * Get the number of selected nodes.
19944      * @return {Number}
19945      */
19946     getSelectionCount : function(){
19947         return this.selections.length;
19948     },
19949
19950     /**
19951      * Get the currently selected nodes.
19952      * @return {Array} An array of HTMLElements
19953      */
19954     getSelectedNodes : function(){
19955         return this.selections;
19956     },
19957
19958     /**
19959      * Get the indexes of the selected nodes.
19960      * @return {Array}
19961      */
19962     getSelectedIndexes : function(){
19963         var indexes = [], s = this.selections;
19964         for(var i = 0, len = s.length; i < len; i++){
19965             indexes.push(s[i].nodeIndex);
19966         }
19967         return indexes;
19968     },
19969
19970     /**
19971      * Clear all selections
19972      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19973      */
19974     clearSelections : function(suppressEvent){
19975         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19976             this.cmp.elements = this.selections;
19977             this.cmp.removeClass(this.selectedClass);
19978             this.selections = [];
19979             if(!suppressEvent){
19980                 this.fireEvent("selectionchange", this, this.selections);
19981             }
19982         }
19983     },
19984
19985     /**
19986      * Returns true if the passed node is selected
19987      * @param {HTMLElement/Number} node The node or node index
19988      * @return {Boolean}
19989      */
19990     isSelected : function(node){
19991         var s = this.selections;
19992         if(s.length < 1){
19993             return false;
19994         }
19995         node = this.getNode(node);
19996         return s.indexOf(node) !== -1;
19997     },
19998
19999     /**
20000      * Selects nodes.
20001      * @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
20002      * @param {Boolean} keepExisting (optional) true to keep existing selections
20003      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20004      */
20005     select : function(nodeInfo, keepExisting, suppressEvent){
20006         if(nodeInfo instanceof Array){
20007             if(!keepExisting){
20008                 this.clearSelections(true);
20009             }
20010             for(var i = 0, len = nodeInfo.length; i < len; i++){
20011                 this.select(nodeInfo[i], true, true);
20012             }
20013             return;
20014         } 
20015         var node = this.getNode(nodeInfo);
20016         if(!node || this.isSelected(node)){
20017             return; // already selected.
20018         }
20019         if(!keepExisting){
20020             this.clearSelections(true);
20021         }
20022         
20023         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20024             Roo.fly(node).addClass(this.selectedClass);
20025             this.selections.push(node);
20026             if(!suppressEvent){
20027                 this.fireEvent("selectionchange", this, this.selections);
20028             }
20029         }
20030         
20031         
20032     },
20033       /**
20034      * Unselects nodes.
20035      * @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
20036      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20037      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20038      */
20039     unselect : function(nodeInfo, keepExisting, suppressEvent)
20040     {
20041         if(nodeInfo instanceof Array){
20042             Roo.each(this.selections, function(s) {
20043                 this.unselect(s, nodeInfo);
20044             }, this);
20045             return;
20046         }
20047         var node = this.getNode(nodeInfo);
20048         if(!node || !this.isSelected(node)){
20049             //Roo.log("not selected");
20050             return; // not selected.
20051         }
20052         // fireevent???
20053         var ns = [];
20054         Roo.each(this.selections, function(s) {
20055             if (s == node ) {
20056                 Roo.fly(node).removeClass(this.selectedClass);
20057
20058                 return;
20059             }
20060             ns.push(s);
20061         },this);
20062         
20063         this.selections= ns;
20064         this.fireEvent("selectionchange", this, this.selections);
20065     },
20066
20067     /**
20068      * Gets a template node.
20069      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20070      * @return {HTMLElement} The node or null if it wasn't found
20071      */
20072     getNode : function(nodeInfo){
20073         if(typeof nodeInfo == "string"){
20074             return document.getElementById(nodeInfo);
20075         }else if(typeof nodeInfo == "number"){
20076             return this.nodes[nodeInfo];
20077         }
20078         return nodeInfo;
20079     },
20080
20081     /**
20082      * Gets a range template nodes.
20083      * @param {Number} startIndex
20084      * @param {Number} endIndex
20085      * @return {Array} An array of nodes
20086      */
20087     getNodes : function(start, end){
20088         var ns = this.nodes;
20089         start = start || 0;
20090         end = typeof end == "undefined" ? ns.length - 1 : end;
20091         var nodes = [];
20092         if(start <= end){
20093             for(var i = start; i <= end; i++){
20094                 nodes.push(ns[i]);
20095             }
20096         } else{
20097             for(var i = start; i >= end; i--){
20098                 nodes.push(ns[i]);
20099             }
20100         }
20101         return nodes;
20102     },
20103
20104     /**
20105      * Finds the index of the passed node
20106      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20107      * @return {Number} The index of the node or -1
20108      */
20109     indexOf : function(node){
20110         node = this.getNode(node);
20111         if(typeof node.nodeIndex == "number"){
20112             return node.nodeIndex;
20113         }
20114         var ns = this.nodes;
20115         for(var i = 0, len = ns.length; i < len; i++){
20116             if(ns[i] == node){
20117                 return i;
20118             }
20119         }
20120         return -1;
20121     }
20122 });
20123 /*
20124  * - LGPL
20125  *
20126  * based on jquery fullcalendar
20127  * 
20128  */
20129
20130 Roo.bootstrap = Roo.bootstrap || {};
20131 /**
20132  * @class Roo.bootstrap.Calendar
20133  * @extends Roo.bootstrap.Component
20134  * Bootstrap Calendar class
20135  * @cfg {Boolean} loadMask (true|false) default false
20136  * @cfg {Object} header generate the user specific header of the calendar, default false
20137
20138  * @constructor
20139  * Create a new Container
20140  * @param {Object} config The config object
20141  */
20142
20143
20144
20145 Roo.bootstrap.Calendar = function(config){
20146     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20147      this.addEvents({
20148         /**
20149              * @event select
20150              * Fires when a date is selected
20151              * @param {DatePicker} this
20152              * @param {Date} date The selected date
20153              */
20154         'select': true,
20155         /**
20156              * @event monthchange
20157              * Fires when the displayed month changes 
20158              * @param {DatePicker} this
20159              * @param {Date} date The selected month
20160              */
20161         'monthchange': true,
20162         /**
20163              * @event evententer
20164              * Fires when mouse over an event
20165              * @param {Calendar} this
20166              * @param {event} Event
20167              */
20168         'evententer': true,
20169         /**
20170              * @event eventleave
20171              * Fires when the mouse leaves an
20172              * @param {Calendar} this
20173              * @param {event}
20174              */
20175         'eventleave': true,
20176         /**
20177              * @event eventclick
20178              * Fires when the mouse click an
20179              * @param {Calendar} this
20180              * @param {event}
20181              */
20182         'eventclick': true
20183         
20184     });
20185
20186 };
20187
20188 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20189     
20190      /**
20191      * @cfg {Number} startDay
20192      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20193      */
20194     startDay : 0,
20195     
20196     loadMask : false,
20197     
20198     header : false,
20199       
20200     getAutoCreate : function(){
20201         
20202         
20203         var fc_button = function(name, corner, style, content ) {
20204             return Roo.apply({},{
20205                 tag : 'span',
20206                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20207                          (corner.length ?
20208                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20209                             ''
20210                         ),
20211                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20212                 unselectable: 'on'
20213             });
20214         };
20215         
20216         var header = {};
20217         
20218         if(!this.header){
20219             header = {
20220                 tag : 'table',
20221                 cls : 'fc-header',
20222                 style : 'width:100%',
20223                 cn : [
20224                     {
20225                         tag: 'tr',
20226                         cn : [
20227                             {
20228                                 tag : 'td',
20229                                 cls : 'fc-header-left',
20230                                 cn : [
20231                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20232                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20233                                     { tag: 'span', cls: 'fc-header-space' },
20234                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20235
20236
20237                                 ]
20238                             },
20239
20240                             {
20241                                 tag : 'td',
20242                                 cls : 'fc-header-center',
20243                                 cn : [
20244                                     {
20245                                         tag: 'span',
20246                                         cls: 'fc-header-title',
20247                                         cn : {
20248                                             tag: 'H2',
20249                                             html : 'month / year'
20250                                         }
20251                                     }
20252
20253                                 ]
20254                             },
20255                             {
20256                                 tag : 'td',
20257                                 cls : 'fc-header-right',
20258                                 cn : [
20259                               /*      fc_button('month', 'left', '', 'month' ),
20260                                     fc_button('week', '', '', 'week' ),
20261                                     fc_button('day', 'right', '', 'day' )
20262                                 */    
20263
20264                                 ]
20265                             }
20266
20267                         ]
20268                     }
20269                 ]
20270             };
20271         }
20272         
20273         header = this.header;
20274         
20275        
20276         var cal_heads = function() {
20277             var ret = [];
20278             // fixme - handle this.
20279             
20280             for (var i =0; i < Date.dayNames.length; i++) {
20281                 var d = Date.dayNames[i];
20282                 ret.push({
20283                     tag: 'th',
20284                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20285                     html : d.substring(0,3)
20286                 });
20287                 
20288             }
20289             ret[0].cls += ' fc-first';
20290             ret[6].cls += ' fc-last';
20291             return ret;
20292         };
20293         var cal_cell = function(n) {
20294             return  {
20295                 tag: 'td',
20296                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20297                 cn : [
20298                     {
20299                         cn : [
20300                             {
20301                                 cls: 'fc-day-number',
20302                                 html: 'D'
20303                             },
20304                             {
20305                                 cls: 'fc-day-content',
20306                              
20307                                 cn : [
20308                                      {
20309                                         style: 'position: relative;' // height: 17px;
20310                                     }
20311                                 ]
20312                             }
20313                             
20314                             
20315                         ]
20316                     }
20317                 ]
20318                 
20319             }
20320         };
20321         var cal_rows = function() {
20322             
20323             var ret = [];
20324             for (var r = 0; r < 6; r++) {
20325                 var row= {
20326                     tag : 'tr',
20327                     cls : 'fc-week',
20328                     cn : []
20329                 };
20330                 
20331                 for (var i =0; i < Date.dayNames.length; i++) {
20332                     var d = Date.dayNames[i];
20333                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20334
20335                 }
20336                 row.cn[0].cls+=' fc-first';
20337                 row.cn[0].cn[0].style = 'min-height:90px';
20338                 row.cn[6].cls+=' fc-last';
20339                 ret.push(row);
20340                 
20341             }
20342             ret[0].cls += ' fc-first';
20343             ret[4].cls += ' fc-prev-last';
20344             ret[5].cls += ' fc-last';
20345             return ret;
20346             
20347         };
20348         
20349         var cal_table = {
20350             tag: 'table',
20351             cls: 'fc-border-separate',
20352             style : 'width:100%',
20353             cellspacing  : 0,
20354             cn : [
20355                 { 
20356                     tag: 'thead',
20357                     cn : [
20358                         { 
20359                             tag: 'tr',
20360                             cls : 'fc-first fc-last',
20361                             cn : cal_heads()
20362                         }
20363                     ]
20364                 },
20365                 { 
20366                     tag: 'tbody',
20367                     cn : cal_rows()
20368                 }
20369                   
20370             ]
20371         };
20372          
20373          var cfg = {
20374             cls : 'fc fc-ltr',
20375             cn : [
20376                 header,
20377                 {
20378                     cls : 'fc-content',
20379                     style : "position: relative;",
20380                     cn : [
20381                         {
20382                             cls : 'fc-view fc-view-month fc-grid',
20383                             style : 'position: relative',
20384                             unselectable : 'on',
20385                             cn : [
20386                                 {
20387                                     cls : 'fc-event-container',
20388                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20389                                 },
20390                                 cal_table
20391                             ]
20392                         }
20393                     ]
20394     
20395                 }
20396            ] 
20397             
20398         };
20399         
20400          
20401         
20402         return cfg;
20403     },
20404     
20405     
20406     initEvents : function()
20407     {
20408         if(!this.store){
20409             throw "can not find store for calendar";
20410         }
20411         
20412         var mark = {
20413             tag: "div",
20414             cls:"x-dlg-mask",
20415             style: "text-align:center",
20416             cn: [
20417                 {
20418                     tag: "div",
20419                     style: "background-color:white;width:50%;margin:250 auto",
20420                     cn: [
20421                         {
20422                             tag: "img",
20423                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20424                         },
20425                         {
20426                             tag: "span",
20427                             html: "Loading"
20428                         }
20429                         
20430                     ]
20431                 }
20432             ]
20433         };
20434         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20435         
20436         var size = this.el.select('.fc-content', true).first().getSize();
20437         this.maskEl.setSize(size.width, size.height);
20438         this.maskEl.enableDisplayMode("block");
20439         if(!this.loadMask){
20440             this.maskEl.hide();
20441         }
20442         
20443         this.store = Roo.factory(this.store, Roo.data);
20444         this.store.on('load', this.onLoad, this);
20445         this.store.on('beforeload', this.onBeforeLoad, this);
20446         
20447         this.resize();
20448         
20449         this.cells = this.el.select('.fc-day',true);
20450         //Roo.log(this.cells);
20451         this.textNodes = this.el.query('.fc-day-number');
20452         this.cells.addClassOnOver('fc-state-hover');
20453         
20454         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20455         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20456         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20457         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20458         
20459         this.on('monthchange', this.onMonthChange, this);
20460         
20461         this.update(new Date().clearTime());
20462     },
20463     
20464     resize : function() {
20465         var sz  = this.el.getSize();
20466         
20467         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20468         this.el.select('.fc-day-content div',true).setHeight(34);
20469     },
20470     
20471     
20472     // private
20473     showPrevMonth : function(e){
20474         this.update(this.activeDate.add("mo", -1));
20475     },
20476     showToday : function(e){
20477         this.update(new Date().clearTime());
20478     },
20479     // private
20480     showNextMonth : function(e){
20481         this.update(this.activeDate.add("mo", 1));
20482     },
20483
20484     // private
20485     showPrevYear : function(){
20486         this.update(this.activeDate.add("y", -1));
20487     },
20488
20489     // private
20490     showNextYear : function(){
20491         this.update(this.activeDate.add("y", 1));
20492     },
20493
20494     
20495    // private
20496     update : function(date)
20497     {
20498         var vd = this.activeDate;
20499         this.activeDate = date;
20500 //        if(vd && this.el){
20501 //            var t = date.getTime();
20502 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20503 //                Roo.log('using add remove');
20504 //                
20505 //                this.fireEvent('monthchange', this, date);
20506 //                
20507 //                this.cells.removeClass("fc-state-highlight");
20508 //                this.cells.each(function(c){
20509 //                   if(c.dateValue == t){
20510 //                       c.addClass("fc-state-highlight");
20511 //                       setTimeout(function(){
20512 //                            try{c.dom.firstChild.focus();}catch(e){}
20513 //                       }, 50);
20514 //                       return false;
20515 //                   }
20516 //                   return true;
20517 //                });
20518 //                return;
20519 //            }
20520 //        }
20521         
20522         var days = date.getDaysInMonth();
20523         
20524         var firstOfMonth = date.getFirstDateOfMonth();
20525         var startingPos = firstOfMonth.getDay()-this.startDay;
20526         
20527         if(startingPos < this.startDay){
20528             startingPos += 7;
20529         }
20530         
20531         var pm = date.add(Date.MONTH, -1);
20532         var prevStart = pm.getDaysInMonth()-startingPos;
20533 //        
20534         this.cells = this.el.select('.fc-day',true);
20535         this.textNodes = this.el.query('.fc-day-number');
20536         this.cells.addClassOnOver('fc-state-hover');
20537         
20538         var cells = this.cells.elements;
20539         var textEls = this.textNodes;
20540         
20541         Roo.each(cells, function(cell){
20542             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20543         });
20544         
20545         days += startingPos;
20546
20547         // convert everything to numbers so it's fast
20548         var day = 86400000;
20549         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20550         //Roo.log(d);
20551         //Roo.log(pm);
20552         //Roo.log(prevStart);
20553         
20554         var today = new Date().clearTime().getTime();
20555         var sel = date.clearTime().getTime();
20556         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20557         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20558         var ddMatch = this.disabledDatesRE;
20559         var ddText = this.disabledDatesText;
20560         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20561         var ddaysText = this.disabledDaysText;
20562         var format = this.format;
20563         
20564         var setCellClass = function(cal, cell){
20565             cell.row = 0;
20566             cell.events = [];
20567             cell.more = [];
20568             //Roo.log('set Cell Class');
20569             cell.title = "";
20570             var t = d.getTime();
20571             
20572             //Roo.log(d);
20573             
20574             cell.dateValue = t;
20575             if(t == today){
20576                 cell.className += " fc-today";
20577                 cell.className += " fc-state-highlight";
20578                 cell.title = cal.todayText;
20579             }
20580             if(t == sel){
20581                 // disable highlight in other month..
20582                 //cell.className += " fc-state-highlight";
20583                 
20584             }
20585             // disabling
20586             if(t < min) {
20587                 cell.className = " fc-state-disabled";
20588                 cell.title = cal.minText;
20589                 return;
20590             }
20591             if(t > max) {
20592                 cell.className = " fc-state-disabled";
20593                 cell.title = cal.maxText;
20594                 return;
20595             }
20596             if(ddays){
20597                 if(ddays.indexOf(d.getDay()) != -1){
20598                     cell.title = ddaysText;
20599                     cell.className = " fc-state-disabled";
20600                 }
20601             }
20602             if(ddMatch && format){
20603                 var fvalue = d.dateFormat(format);
20604                 if(ddMatch.test(fvalue)){
20605                     cell.title = ddText.replace("%0", fvalue);
20606                     cell.className = " fc-state-disabled";
20607                 }
20608             }
20609             
20610             if (!cell.initialClassName) {
20611                 cell.initialClassName = cell.dom.className;
20612             }
20613             
20614             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20615         };
20616
20617         var i = 0;
20618         
20619         for(; i < startingPos; i++) {
20620             textEls[i].innerHTML = (++prevStart);
20621             d.setDate(d.getDate()+1);
20622             
20623             cells[i].className = "fc-past fc-other-month";
20624             setCellClass(this, cells[i]);
20625         }
20626         
20627         var intDay = 0;
20628         
20629         for(; i < days; i++){
20630             intDay = i - startingPos + 1;
20631             textEls[i].innerHTML = (intDay);
20632             d.setDate(d.getDate()+1);
20633             
20634             cells[i].className = ''; // "x-date-active";
20635             setCellClass(this, cells[i]);
20636         }
20637         var extraDays = 0;
20638         
20639         for(; i < 42; i++) {
20640             textEls[i].innerHTML = (++extraDays);
20641             d.setDate(d.getDate()+1);
20642             
20643             cells[i].className = "fc-future fc-other-month";
20644             setCellClass(this, cells[i]);
20645         }
20646         
20647         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20648         
20649         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20650         
20651         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20652         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20653         
20654         if(totalRows != 6){
20655             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20656             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20657         }
20658         
20659         this.fireEvent('monthchange', this, date);
20660         
20661         
20662         /*
20663         if(!this.internalRender){
20664             var main = this.el.dom.firstChild;
20665             var w = main.offsetWidth;
20666             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20667             Roo.fly(main).setWidth(w);
20668             this.internalRender = true;
20669             // opera does not respect the auto grow header center column
20670             // then, after it gets a width opera refuses to recalculate
20671             // without a second pass
20672             if(Roo.isOpera && !this.secondPass){
20673                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20674                 this.secondPass = true;
20675                 this.update.defer(10, this, [date]);
20676             }
20677         }
20678         */
20679         
20680     },
20681     
20682     findCell : function(dt) {
20683         dt = dt.clearTime().getTime();
20684         var ret = false;
20685         this.cells.each(function(c){
20686             //Roo.log("check " +c.dateValue + '?=' + dt);
20687             if(c.dateValue == dt){
20688                 ret = c;
20689                 return false;
20690             }
20691             return true;
20692         });
20693         
20694         return ret;
20695     },
20696     
20697     findCells : function(ev) {
20698         var s = ev.start.clone().clearTime().getTime();
20699        // Roo.log(s);
20700         var e= ev.end.clone().clearTime().getTime();
20701        // Roo.log(e);
20702         var ret = [];
20703         this.cells.each(function(c){
20704              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20705             
20706             if(c.dateValue > e){
20707                 return ;
20708             }
20709             if(c.dateValue < s){
20710                 return ;
20711             }
20712             ret.push(c);
20713         });
20714         
20715         return ret;    
20716     },
20717     
20718 //    findBestRow: function(cells)
20719 //    {
20720 //        var ret = 0;
20721 //        
20722 //        for (var i =0 ; i < cells.length;i++) {
20723 //            ret  = Math.max(cells[i].rows || 0,ret);
20724 //        }
20725 //        return ret;
20726 //        
20727 //    },
20728     
20729     
20730     addItem : function(ev)
20731     {
20732         // look for vertical location slot in
20733         var cells = this.findCells(ev);
20734         
20735 //        ev.row = this.findBestRow(cells);
20736         
20737         // work out the location.
20738         
20739         var crow = false;
20740         var rows = [];
20741         for(var i =0; i < cells.length; i++) {
20742             
20743             cells[i].row = cells[0].row;
20744             
20745             if(i == 0){
20746                 cells[i].row = cells[i].row + 1;
20747             }
20748             
20749             if (!crow) {
20750                 crow = {
20751                     start : cells[i],
20752                     end :  cells[i]
20753                 };
20754                 continue;
20755             }
20756             if (crow.start.getY() == cells[i].getY()) {
20757                 // on same row.
20758                 crow.end = cells[i];
20759                 continue;
20760             }
20761             // different row.
20762             rows.push(crow);
20763             crow = {
20764                 start: cells[i],
20765                 end : cells[i]
20766             };
20767             
20768         }
20769         
20770         rows.push(crow);
20771         ev.els = [];
20772         ev.rows = rows;
20773         ev.cells = cells;
20774         
20775         cells[0].events.push(ev);
20776         
20777         this.calevents.push(ev);
20778     },
20779     
20780     clearEvents: function() {
20781         
20782         if(!this.calevents){
20783             return;
20784         }
20785         
20786         Roo.each(this.cells.elements, function(c){
20787             c.row = 0;
20788             c.events = [];
20789             c.more = [];
20790         });
20791         
20792         Roo.each(this.calevents, function(e) {
20793             Roo.each(e.els, function(el) {
20794                 el.un('mouseenter' ,this.onEventEnter, this);
20795                 el.un('mouseleave' ,this.onEventLeave, this);
20796                 el.remove();
20797             },this);
20798         },this);
20799         
20800         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20801             e.remove();
20802         });
20803         
20804     },
20805     
20806     renderEvents: function()
20807     {   
20808         var _this = this;
20809         
20810         this.cells.each(function(c) {
20811             
20812             if(c.row < 5){
20813                 return;
20814             }
20815             
20816             var ev = c.events;
20817             
20818             var r = 4;
20819             if(c.row != c.events.length){
20820                 r = 4 - (4 - (c.row - c.events.length));
20821             }
20822             
20823             c.events = ev.slice(0, r);
20824             c.more = ev.slice(r);
20825             
20826             if(c.more.length && c.more.length == 1){
20827                 c.events.push(c.more.pop());
20828             }
20829             
20830             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20831             
20832         });
20833             
20834         this.cells.each(function(c) {
20835             
20836             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20837             
20838             
20839             for (var e = 0; e < c.events.length; e++){
20840                 var ev = c.events[e];
20841                 var rows = ev.rows;
20842                 
20843                 for(var i = 0; i < rows.length; i++) {
20844                 
20845                     // how many rows should it span..
20846
20847                     var  cfg = {
20848                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20849                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20850
20851                         unselectable : "on",
20852                         cn : [
20853                             {
20854                                 cls: 'fc-event-inner',
20855                                 cn : [
20856     //                                {
20857     //                                  tag:'span',
20858     //                                  cls: 'fc-event-time',
20859     //                                  html : cells.length > 1 ? '' : ev.time
20860     //                                },
20861                                     {
20862                                       tag:'span',
20863                                       cls: 'fc-event-title',
20864                                       html : String.format('{0}', ev.title)
20865                                     }
20866
20867
20868                                 ]
20869                             },
20870                             {
20871                                 cls: 'ui-resizable-handle ui-resizable-e',
20872                                 html : '&nbsp;&nbsp;&nbsp'
20873                             }
20874
20875                         ]
20876                     };
20877
20878                     if (i == 0) {
20879                         cfg.cls += ' fc-event-start';
20880                     }
20881                     if ((i+1) == rows.length) {
20882                         cfg.cls += ' fc-event-end';
20883                     }
20884
20885                     var ctr = _this.el.select('.fc-event-container',true).first();
20886                     var cg = ctr.createChild(cfg);
20887
20888                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20889                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20890
20891                     var r = (c.more.length) ? 1 : 0;
20892                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20893                     cg.setWidth(ebox.right - sbox.x -2);
20894
20895                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20896                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20897                     cg.on('click', _this.onEventClick, _this, ev);
20898
20899                     ev.els.push(cg);
20900                     
20901                 }
20902                 
20903             }
20904             
20905             
20906             if(c.more.length){
20907                 var  cfg = {
20908                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20909                     style : 'position: absolute',
20910                     unselectable : "on",
20911                     cn : [
20912                         {
20913                             cls: 'fc-event-inner',
20914                             cn : [
20915                                 {
20916                                   tag:'span',
20917                                   cls: 'fc-event-title',
20918                                   html : 'More'
20919                                 }
20920
20921
20922                             ]
20923                         },
20924                         {
20925                             cls: 'ui-resizable-handle ui-resizable-e',
20926                             html : '&nbsp;&nbsp;&nbsp'
20927                         }
20928
20929                     ]
20930                 };
20931
20932                 var ctr = _this.el.select('.fc-event-container',true).first();
20933                 var cg = ctr.createChild(cfg);
20934
20935                 var sbox = c.select('.fc-day-content',true).first().getBox();
20936                 var ebox = c.select('.fc-day-content',true).first().getBox();
20937                 //Roo.log(cg);
20938                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20939                 cg.setWidth(ebox.right - sbox.x -2);
20940
20941                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20942                 
20943             }
20944             
20945         });
20946         
20947         
20948         
20949     },
20950     
20951     onEventEnter: function (e, el,event,d) {
20952         this.fireEvent('evententer', this, el, event);
20953     },
20954     
20955     onEventLeave: function (e, el,event,d) {
20956         this.fireEvent('eventleave', this, el, event);
20957     },
20958     
20959     onEventClick: function (e, el,event,d) {
20960         this.fireEvent('eventclick', this, el, event);
20961     },
20962     
20963     onMonthChange: function () {
20964         this.store.load();
20965     },
20966     
20967     onMoreEventClick: function(e, el, more)
20968     {
20969         var _this = this;
20970         
20971         this.calpopover.placement = 'right';
20972         this.calpopover.setTitle('More');
20973         
20974         this.calpopover.setContent('');
20975         
20976         var ctr = this.calpopover.el.select('.popover-content', true).first();
20977         
20978         Roo.each(more, function(m){
20979             var cfg = {
20980                 cls : 'fc-event-hori fc-event-draggable',
20981                 html : m.title
20982             };
20983             var cg = ctr.createChild(cfg);
20984             
20985             cg.on('click', _this.onEventClick, _this, m);
20986         });
20987         
20988         this.calpopover.show(el);
20989         
20990         
20991     },
20992     
20993     onLoad: function () 
20994     {   
20995         this.calevents = [];
20996         var cal = this;
20997         
20998         if(this.store.getCount() > 0){
20999             this.store.data.each(function(d){
21000                cal.addItem({
21001                     id : d.data.id,
21002                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21003                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21004                     time : d.data.start_time,
21005                     title : d.data.title,
21006                     description : d.data.description,
21007                     venue : d.data.venue
21008                 });
21009             });
21010         }
21011         
21012         this.renderEvents();
21013         
21014         if(this.calevents.length && this.loadMask){
21015             this.maskEl.hide();
21016         }
21017     },
21018     
21019     onBeforeLoad: function()
21020     {
21021         this.clearEvents();
21022         if(this.loadMask){
21023             this.maskEl.show();
21024         }
21025     }
21026 });
21027
21028  
21029  /*
21030  * - LGPL
21031  *
21032  * element
21033  * 
21034  */
21035
21036 /**
21037  * @class Roo.bootstrap.Popover
21038  * @extends Roo.bootstrap.Component
21039  * Bootstrap Popover class
21040  * @cfg {String} html contents of the popover   (or false to use children..)
21041  * @cfg {String} title of popover (or false to hide)
21042  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21043  * @cfg {String} trigger click || hover (or false to trigger manually)
21044  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21045  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21046  *      - if false and it has a 'parent' then it will be automatically added to that element
21047  *      - if string - Roo.get  will be called 
21048  * @cfg {Number} delay - delay before showing
21049  
21050  * @constructor
21051  * Create a new Popover
21052  * @param {Object} config The config object
21053  */
21054
21055 Roo.bootstrap.Popover = function(config){
21056     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21057     
21058     this.addEvents({
21059         // raw events
21060          /**
21061          * @event show
21062          * After the popover show
21063          * 
21064          * @param {Roo.bootstrap.Popover} this
21065          */
21066         "show" : true,
21067         /**
21068          * @event hide
21069          * After the popover hide
21070          * 
21071          * @param {Roo.bootstrap.Popover} this
21072          */
21073         "hide" : true
21074     });
21075 };
21076
21077 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21078     
21079     title: false,
21080     html: false,
21081     
21082     placement : 'right',
21083     trigger : 'hover', // hover
21084     modal : false,
21085     delay : 0,
21086     
21087     over: false,
21088     
21089     can_build_overlaid : false,
21090     
21091     maskEl : false, // the mask element
21092     headerEl : false,
21093     contentEl : false,
21094     alignEl : false, // when show is called with an element - this get's stored.
21095     
21096     getChildContainer : function()
21097     {
21098         return this.contentEl;
21099         
21100     },
21101     getPopoverHeader : function()
21102     {
21103         this.title = true; // flag not to hide it..
21104         this.headerEl.addClass('p-0');
21105         return this.headerEl
21106     },
21107     
21108     
21109     getAutoCreate : function(){
21110          
21111         var cfg = {
21112            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21113            style: 'display:block',
21114            cn : [
21115                 {
21116                     cls : 'arrow'
21117                 },
21118                 {
21119                     cls : 'popover-inner ',
21120                     cn : [
21121                         {
21122                             tag: 'h3',
21123                             cls: 'popover-title popover-header',
21124                             html : this.title === false ? '' : this.title
21125                         },
21126                         {
21127                             cls : 'popover-content popover-body '  + (this.cls || ''),
21128                             html : this.html || ''
21129                         }
21130                     ]
21131                     
21132                 }
21133            ]
21134         };
21135         
21136         return cfg;
21137     },
21138     /**
21139      * @param {string} the title
21140      */
21141     setTitle: function(str)
21142     {
21143         this.title = str;
21144         if (this.el) {
21145             this.headerEl.dom.innerHTML = str;
21146         }
21147         
21148     },
21149     /**
21150      * @param {string} the body content
21151      */
21152     setContent: function(str)
21153     {
21154         this.html = str;
21155         if (this.contentEl) {
21156             this.contentEl.dom.innerHTML = str;
21157         }
21158         
21159     },
21160     // as it get's added to the bottom of the page.
21161     onRender : function(ct, position)
21162     {
21163         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21164         
21165         
21166         
21167         if(!this.el){
21168             var cfg = Roo.apply({},  this.getAutoCreate());
21169             cfg.id = Roo.id();
21170             
21171             if (this.cls) {
21172                 cfg.cls += ' ' + this.cls;
21173             }
21174             if (this.style) {
21175                 cfg.style = this.style;
21176             }
21177             //Roo.log("adding to ");
21178             this.el = Roo.get(document.body).createChild(cfg, position);
21179 //            Roo.log(this.el);
21180         }
21181         
21182         this.contentEl = this.el.select('.popover-content',true).first();
21183         this.headerEl =  this.el.select('.popover-title',true).first();
21184         
21185         var nitems = [];
21186         if(typeof(this.items) != 'undefined'){
21187             var items = this.items;
21188             delete this.items;
21189
21190             for(var i =0;i < items.length;i++) {
21191                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21192             }
21193         }
21194
21195         this.items = nitems;
21196         
21197         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21198         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21199         
21200         
21201         
21202         this.initEvents();
21203     },
21204     
21205     resizeMask : function()
21206     {
21207         this.maskEl.setSize(
21208             Roo.lib.Dom.getViewWidth(true),
21209             Roo.lib.Dom.getViewHeight(true)
21210         );
21211     },
21212     
21213     initEvents : function()
21214     {
21215         
21216         if (!this.modal) { 
21217             Roo.bootstrap.Popover.register(this);
21218         }
21219          
21220         this.arrowEl = this.el.select('.arrow',true).first();
21221         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21222         this.el.enableDisplayMode('block');
21223         this.el.hide();
21224  
21225         
21226         if (this.over === false && !this.parent()) {
21227             return; 
21228         }
21229         if (this.triggers === false) {
21230             return;
21231         }
21232          
21233         // support parent
21234         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21235         var triggers = this.trigger ? this.trigger.split(' ') : [];
21236         Roo.each(triggers, function(trigger) {
21237         
21238             if (trigger == 'click') {
21239                 on_el.on('click', this.toggle, this);
21240             } else if (trigger != 'manual') {
21241                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21242                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21243       
21244                 on_el.on(eventIn  ,this.enter, this);
21245                 on_el.on(eventOut, this.leave, this);
21246             }
21247         }, this);
21248     },
21249     
21250     
21251     // private
21252     timeout : null,
21253     hoverState : null,
21254     
21255     toggle : function () {
21256         this.hoverState == 'in' ? this.leave() : this.enter();
21257     },
21258     
21259     enter : function () {
21260         
21261         clearTimeout(this.timeout);
21262     
21263         this.hoverState = 'in';
21264     
21265         if (!this.delay || !this.delay.show) {
21266             this.show();
21267             return;
21268         }
21269         var _t = this;
21270         this.timeout = setTimeout(function () {
21271             if (_t.hoverState == 'in') {
21272                 _t.show();
21273             }
21274         }, this.delay.show)
21275     },
21276     
21277     leave : function() {
21278         clearTimeout(this.timeout);
21279     
21280         this.hoverState = 'out';
21281     
21282         if (!this.delay || !this.delay.hide) {
21283             this.hide();
21284             return;
21285         }
21286         var _t = this;
21287         this.timeout = setTimeout(function () {
21288             if (_t.hoverState == 'out') {
21289                 _t.hide();
21290             }
21291         }, this.delay.hide)
21292     },
21293     /**
21294      * Show the popover
21295      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21296      * @param {string} (left|right|top|bottom) position
21297      */
21298     show : function (on_el, placement)
21299     {
21300         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21301         on_el = on_el || false; // default to false
21302          
21303         if (!on_el) {
21304             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21305                 on_el = this.parent().el;
21306             } else if (this.over) {
21307                 on_el = Roo.get(this.over);
21308             }
21309             
21310         }
21311         
21312         this.alignEl = Roo.get( on_el );
21313
21314         if (!this.el) {
21315             this.render(document.body);
21316         }
21317         
21318         
21319          
21320         
21321         if (this.title === false) {
21322             this.headerEl.hide();
21323         }
21324         
21325        
21326         this.el.show();
21327         this.el.dom.style.display = 'block';
21328          
21329  
21330         if (this.alignEl) {
21331             this.updatePosition(this.placement, true);
21332              
21333         } else {
21334             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21335             var es = this.el.getSize();
21336             var x = Roo.lib.Dom.getViewWidth()/2;
21337             var y = Roo.lib.Dom.getViewHeight()/2;
21338             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21339             
21340         }
21341
21342         
21343         //var arrow = this.el.select('.arrow',true).first();
21344         //arrow.set(align[2], 
21345         
21346         this.el.addClass('in');
21347         
21348          
21349         
21350         this.hoverState = 'in';
21351         
21352         if (this.modal) {
21353             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21354             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21355             this.maskEl.dom.style.display = 'block';
21356             this.maskEl.addClass('show');
21357         }
21358         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21359  
21360         this.fireEvent('show', this);
21361         
21362     },
21363     /**
21364      * fire this manually after loading a grid in the table for example
21365      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21366      * @param {Boolean} try and move it if we cant get right position.
21367      */
21368     updatePosition : function(placement, try_move)
21369     {
21370         // allow for calling with no parameters
21371         placement = placement   ? placement :  this.placement;
21372         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21373         
21374         this.el.removeClass([
21375             'fade','top','bottom', 'left', 'right','in',
21376             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21377         ]);
21378         this.el.addClass(placement + ' bs-popover-' + placement);
21379         
21380         if (!this.alignEl ) {
21381             return false;
21382         }
21383         
21384         switch (placement) {
21385             case 'right':
21386                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21387                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21388                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21389                     //normal display... or moved up/down.
21390                     this.el.setXY(offset);
21391                     var xy = this.alignEl.getAnchorXY('tr', false);
21392                     xy[0]+=2;xy[1]+=5;
21393                     this.arrowEl.setXY(xy);
21394                     return true;
21395                 }
21396                 // continue through...
21397                 return this.updatePosition('left', false);
21398                 
21399             
21400             case 'left':
21401                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21402                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21403                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21404                     //normal display... or moved up/down.
21405                     this.el.setXY(offset);
21406                     var xy = this.alignEl.getAnchorXY('tl', false);
21407                     xy[0]-=10;xy[1]+=5; // << fix me
21408                     this.arrowEl.setXY(xy);
21409                     return true;
21410                 }
21411                 // call self...
21412                 return this.updatePosition('right', false);
21413             
21414             case 'top':
21415                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21416                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21417                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21418                     //normal display... or moved up/down.
21419                     this.el.setXY(offset);
21420                     var xy = this.alignEl.getAnchorXY('t', false);
21421                     xy[1]-=10; // << fix me
21422                     this.arrowEl.setXY(xy);
21423                     return true;
21424                 }
21425                 // fall through
21426                return this.updatePosition('bottom', false);
21427             
21428             case 'bottom':
21429                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21430                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21431                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21432                     //normal display... or moved up/down.
21433                     this.el.setXY(offset);
21434                     var xy = this.alignEl.getAnchorXY('b', false);
21435                      xy[1]+=2; // << fix me
21436                     this.arrowEl.setXY(xy);
21437                     return true;
21438                 }
21439                 // fall through
21440                 return this.updatePosition('top', false);
21441                 
21442             
21443         }
21444         
21445         
21446         return false;
21447     },
21448     
21449     hide : function()
21450     {
21451         this.el.setXY([0,0]);
21452         this.el.removeClass('in');
21453         this.el.hide();
21454         this.hoverState = null;
21455         this.maskEl.hide(); // always..
21456         this.fireEvent('hide', this);
21457     }
21458     
21459 });
21460
21461
21462 Roo.apply(Roo.bootstrap.Popover, {
21463
21464     alignment : {
21465         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21466         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21467         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21468         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21469     },
21470     
21471     zIndex : 20001,
21472
21473     clickHander : false,
21474     
21475     
21476
21477     onMouseDown : function(e)
21478     {
21479         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21480             /// what is nothing is showing..
21481             this.hideAll();
21482         }
21483          
21484     },
21485     
21486     
21487     popups : [],
21488     
21489     register : function(popup)
21490     {
21491         if (!Roo.bootstrap.Popover.clickHandler) {
21492             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21493         }
21494         // hide other popups.
21495         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21496         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21497         this.hideAll(); //<< why?
21498         //this.popups.push(popup);
21499     },
21500     hideAll : function()
21501     {
21502         this.popups.forEach(function(p) {
21503             p.hide();
21504         });
21505     },
21506     onShow : function() {
21507         Roo.bootstrap.Popover.popups.push(this);
21508     },
21509     onHide : function() {
21510         Roo.bootstrap.Popover.popups.remove(this);
21511     } 
21512
21513 });/*
21514  * - LGPL
21515  *
21516  * Card header - holder for the card header elements.
21517  * 
21518  */
21519
21520 /**
21521  * @class Roo.bootstrap.PopoverNav
21522  * @extends Roo.bootstrap.NavGroup
21523  * Bootstrap Popover header navigation class
21524  * @constructor
21525  * Create a new Popover Header Navigation 
21526  * @param {Object} config The config object
21527  */
21528
21529 Roo.bootstrap.PopoverNav = function(config){
21530     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21531 };
21532
21533 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21534     
21535     
21536     container_method : 'getPopoverHeader' 
21537     
21538      
21539     
21540     
21541    
21542 });
21543
21544  
21545
21546  /*
21547  * - LGPL
21548  *
21549  * Progress
21550  * 
21551  */
21552
21553 /**
21554  * @class Roo.bootstrap.Progress
21555  * @extends Roo.bootstrap.Component
21556  * Bootstrap Progress class
21557  * @cfg {Boolean} striped striped of the progress bar
21558  * @cfg {Boolean} active animated of the progress bar
21559  * 
21560  * 
21561  * @constructor
21562  * Create a new Progress
21563  * @param {Object} config The config object
21564  */
21565
21566 Roo.bootstrap.Progress = function(config){
21567     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21568 };
21569
21570 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21571     
21572     striped : false,
21573     active: false,
21574     
21575     getAutoCreate : function(){
21576         var cfg = {
21577             tag: 'div',
21578             cls: 'progress'
21579         };
21580         
21581         
21582         if(this.striped){
21583             cfg.cls += ' progress-striped';
21584         }
21585       
21586         if(this.active){
21587             cfg.cls += ' active';
21588         }
21589         
21590         
21591         return cfg;
21592     }
21593    
21594 });
21595
21596  
21597
21598  /*
21599  * - LGPL
21600  *
21601  * ProgressBar
21602  * 
21603  */
21604
21605 /**
21606  * @class Roo.bootstrap.ProgressBar
21607  * @extends Roo.bootstrap.Component
21608  * Bootstrap ProgressBar class
21609  * @cfg {Number} aria_valuenow aria-value now
21610  * @cfg {Number} aria_valuemin aria-value min
21611  * @cfg {Number} aria_valuemax aria-value max
21612  * @cfg {String} label label for the progress bar
21613  * @cfg {String} panel (success | info | warning | danger )
21614  * @cfg {String} role role of the progress bar
21615  * @cfg {String} sr_only text
21616  * 
21617  * 
21618  * @constructor
21619  * Create a new ProgressBar
21620  * @param {Object} config The config object
21621  */
21622
21623 Roo.bootstrap.ProgressBar = function(config){
21624     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21625 };
21626
21627 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21628     
21629     aria_valuenow : 0,
21630     aria_valuemin : 0,
21631     aria_valuemax : 100,
21632     label : false,
21633     panel : false,
21634     role : false,
21635     sr_only: false,
21636     
21637     getAutoCreate : function()
21638     {
21639         
21640         var cfg = {
21641             tag: 'div',
21642             cls: 'progress-bar',
21643             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21644         };
21645         
21646         if(this.sr_only){
21647             cfg.cn = {
21648                 tag: 'span',
21649                 cls: 'sr-only',
21650                 html: this.sr_only
21651             }
21652         }
21653         
21654         if(this.role){
21655             cfg.role = this.role;
21656         }
21657         
21658         if(this.aria_valuenow){
21659             cfg['aria-valuenow'] = this.aria_valuenow;
21660         }
21661         
21662         if(this.aria_valuemin){
21663             cfg['aria-valuemin'] = this.aria_valuemin;
21664         }
21665         
21666         if(this.aria_valuemax){
21667             cfg['aria-valuemax'] = this.aria_valuemax;
21668         }
21669         
21670         if(this.label && !this.sr_only){
21671             cfg.html = this.label;
21672         }
21673         
21674         if(this.panel){
21675             cfg.cls += ' progress-bar-' + this.panel;
21676         }
21677         
21678         return cfg;
21679     },
21680     
21681     update : function(aria_valuenow)
21682     {
21683         this.aria_valuenow = aria_valuenow;
21684         
21685         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21686     }
21687    
21688 });
21689
21690  
21691
21692  /*
21693  * - LGPL
21694  *
21695  * column
21696  * 
21697  */
21698
21699 /**
21700  * @class Roo.bootstrap.TabGroup
21701  * @extends Roo.bootstrap.Column
21702  * Bootstrap Column class
21703  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21704  * @cfg {Boolean} carousel true to make the group behave like a carousel
21705  * @cfg {Boolean} bullets show bullets for the panels
21706  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21707  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21708  * @cfg {Boolean} showarrow (true|false) show arrow default true
21709  * 
21710  * @constructor
21711  * Create a new TabGroup
21712  * @param {Object} config The config object
21713  */
21714
21715 Roo.bootstrap.TabGroup = function(config){
21716     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21717     if (!this.navId) {
21718         this.navId = Roo.id();
21719     }
21720     this.tabs = [];
21721     Roo.bootstrap.TabGroup.register(this);
21722     
21723 };
21724
21725 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21726     
21727     carousel : false,
21728     transition : false,
21729     bullets : 0,
21730     timer : 0,
21731     autoslide : false,
21732     slideFn : false,
21733     slideOnTouch : false,
21734     showarrow : true,
21735     
21736     getAutoCreate : function()
21737     {
21738         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21739         
21740         cfg.cls += ' tab-content';
21741         
21742         if (this.carousel) {
21743             cfg.cls += ' carousel slide';
21744             
21745             cfg.cn = [{
21746                cls : 'carousel-inner',
21747                cn : []
21748             }];
21749         
21750             if(this.bullets  && !Roo.isTouch){
21751                 
21752                 var bullets = {
21753                     cls : 'carousel-bullets',
21754                     cn : []
21755                 };
21756                
21757                 if(this.bullets_cls){
21758                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21759                 }
21760                 
21761                 bullets.cn.push({
21762                     cls : 'clear'
21763                 });
21764                 
21765                 cfg.cn[0].cn.push(bullets);
21766             }
21767             
21768             if(this.showarrow){
21769                 cfg.cn[0].cn.push({
21770                     tag : 'div',
21771                     class : 'carousel-arrow',
21772                     cn : [
21773                         {
21774                             tag : 'div',
21775                             class : 'carousel-prev',
21776                             cn : [
21777                                 {
21778                                     tag : 'i',
21779                                     class : 'fa fa-chevron-left'
21780                                 }
21781                             ]
21782                         },
21783                         {
21784                             tag : 'div',
21785                             class : 'carousel-next',
21786                             cn : [
21787                                 {
21788                                     tag : 'i',
21789                                     class : 'fa fa-chevron-right'
21790                                 }
21791                             ]
21792                         }
21793                     ]
21794                 });
21795             }
21796             
21797         }
21798         
21799         return cfg;
21800     },
21801     
21802     initEvents:  function()
21803     {
21804 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21805 //            this.el.on("touchstart", this.onTouchStart, this);
21806 //        }
21807         
21808         if(this.autoslide){
21809             var _this = this;
21810             
21811             this.slideFn = window.setInterval(function() {
21812                 _this.showPanelNext();
21813             }, this.timer);
21814         }
21815         
21816         if(this.showarrow){
21817             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21818             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21819         }
21820         
21821         
21822     },
21823     
21824 //    onTouchStart : function(e, el, o)
21825 //    {
21826 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21827 //            return;
21828 //        }
21829 //        
21830 //        this.showPanelNext();
21831 //    },
21832     
21833     
21834     getChildContainer : function()
21835     {
21836         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21837     },
21838     
21839     /**
21840     * register a Navigation item
21841     * @param {Roo.bootstrap.NavItem} the navitem to add
21842     */
21843     register : function(item)
21844     {
21845         this.tabs.push( item);
21846         item.navId = this.navId; // not really needed..
21847         this.addBullet();
21848     
21849     },
21850     
21851     getActivePanel : function()
21852     {
21853         var r = false;
21854         Roo.each(this.tabs, function(t) {
21855             if (t.active) {
21856                 r = t;
21857                 return false;
21858             }
21859             return null;
21860         });
21861         return r;
21862         
21863     },
21864     getPanelByName : function(n)
21865     {
21866         var r = false;
21867         Roo.each(this.tabs, function(t) {
21868             if (t.tabId == n) {
21869                 r = t;
21870                 return false;
21871             }
21872             return null;
21873         });
21874         return r;
21875     },
21876     indexOfPanel : function(p)
21877     {
21878         var r = false;
21879         Roo.each(this.tabs, function(t,i) {
21880             if (t.tabId == p.tabId) {
21881                 r = i;
21882                 return false;
21883             }
21884             return null;
21885         });
21886         return r;
21887     },
21888     /**
21889      * show a specific panel
21890      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21891      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21892      */
21893     showPanel : function (pan)
21894     {
21895         if(this.transition || typeof(pan) == 'undefined'){
21896             Roo.log("waiting for the transitionend");
21897             return false;
21898         }
21899         
21900         if (typeof(pan) == 'number') {
21901             pan = this.tabs[pan];
21902         }
21903         
21904         if (typeof(pan) == 'string') {
21905             pan = this.getPanelByName(pan);
21906         }
21907         
21908         var cur = this.getActivePanel();
21909         
21910         if(!pan || !cur){
21911             Roo.log('pan or acitve pan is undefined');
21912             return false;
21913         }
21914         
21915         if (pan.tabId == this.getActivePanel().tabId) {
21916             return true;
21917         }
21918         
21919         if (false === cur.fireEvent('beforedeactivate')) {
21920             return false;
21921         }
21922         
21923         if(this.bullets > 0 && !Roo.isTouch){
21924             this.setActiveBullet(this.indexOfPanel(pan));
21925         }
21926         
21927         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21928             
21929             //class="carousel-item carousel-item-next carousel-item-left"
21930             
21931             this.transition = true;
21932             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21933             var lr = dir == 'next' ? 'left' : 'right';
21934             pan.el.addClass(dir); // or prev
21935             pan.el.addClass('carousel-item-' + dir); // or prev
21936             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21937             cur.el.addClass(lr); // or right
21938             pan.el.addClass(lr);
21939             cur.el.addClass('carousel-item-' +lr); // or right
21940             pan.el.addClass('carousel-item-' +lr);
21941             
21942             
21943             var _this = this;
21944             cur.el.on('transitionend', function() {
21945                 Roo.log("trans end?");
21946                 
21947                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21948                 pan.setActive(true);
21949                 
21950                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21951                 cur.setActive(false);
21952                 
21953                 _this.transition = false;
21954                 
21955             }, this, { single:  true } );
21956             
21957             return true;
21958         }
21959         
21960         cur.setActive(false);
21961         pan.setActive(true);
21962         
21963         return true;
21964         
21965     },
21966     showPanelNext : function()
21967     {
21968         var i = this.indexOfPanel(this.getActivePanel());
21969         
21970         if (i >= this.tabs.length - 1 && !this.autoslide) {
21971             return;
21972         }
21973         
21974         if (i >= this.tabs.length - 1 && this.autoslide) {
21975             i = -1;
21976         }
21977         
21978         this.showPanel(this.tabs[i+1]);
21979     },
21980     
21981     showPanelPrev : function()
21982     {
21983         var i = this.indexOfPanel(this.getActivePanel());
21984         
21985         if (i  < 1 && !this.autoslide) {
21986             return;
21987         }
21988         
21989         if (i < 1 && this.autoslide) {
21990             i = this.tabs.length;
21991         }
21992         
21993         this.showPanel(this.tabs[i-1]);
21994     },
21995     
21996     
21997     addBullet: function()
21998     {
21999         if(!this.bullets || Roo.isTouch){
22000             return;
22001         }
22002         var ctr = this.el.select('.carousel-bullets',true).first();
22003         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22004         var bullet = ctr.createChild({
22005             cls : 'bullet bullet-' + i
22006         },ctr.dom.lastChild);
22007         
22008         
22009         var _this = this;
22010         
22011         bullet.on('click', (function(e, el, o, ii, t){
22012
22013             e.preventDefault();
22014
22015             this.showPanel(ii);
22016
22017             if(this.autoslide && this.slideFn){
22018                 clearInterval(this.slideFn);
22019                 this.slideFn = window.setInterval(function() {
22020                     _this.showPanelNext();
22021                 }, this.timer);
22022             }
22023
22024         }).createDelegate(this, [i, bullet], true));
22025                 
22026         
22027     },
22028      
22029     setActiveBullet : function(i)
22030     {
22031         if(Roo.isTouch){
22032             return;
22033         }
22034         
22035         Roo.each(this.el.select('.bullet', true).elements, function(el){
22036             el.removeClass('selected');
22037         });
22038
22039         var bullet = this.el.select('.bullet-' + i, true).first();
22040         
22041         if(!bullet){
22042             return;
22043         }
22044         
22045         bullet.addClass('selected');
22046     }
22047     
22048     
22049   
22050 });
22051
22052  
22053
22054  
22055  
22056 Roo.apply(Roo.bootstrap.TabGroup, {
22057     
22058     groups: {},
22059      /**
22060     * register a Navigation Group
22061     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22062     */
22063     register : function(navgrp)
22064     {
22065         this.groups[navgrp.navId] = navgrp;
22066         
22067     },
22068     /**
22069     * fetch a Navigation Group based on the navigation ID
22070     * if one does not exist , it will get created.
22071     * @param {string} the navgroup to add
22072     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22073     */
22074     get: function(navId) {
22075         if (typeof(this.groups[navId]) == 'undefined') {
22076             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22077         }
22078         return this.groups[navId] ;
22079     }
22080     
22081     
22082     
22083 });
22084
22085  /*
22086  * - LGPL
22087  *
22088  * TabPanel
22089  * 
22090  */
22091
22092 /**
22093  * @class Roo.bootstrap.TabPanel
22094  * @extends Roo.bootstrap.Component
22095  * Bootstrap TabPanel class
22096  * @cfg {Boolean} active panel active
22097  * @cfg {String} html panel content
22098  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22099  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22100  * @cfg {String} href click to link..
22101  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22102  * 
22103  * 
22104  * @constructor
22105  * Create a new TabPanel
22106  * @param {Object} config The config object
22107  */
22108
22109 Roo.bootstrap.TabPanel = function(config){
22110     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22111     this.addEvents({
22112         /**
22113              * @event changed
22114              * Fires when the active status changes
22115              * @param {Roo.bootstrap.TabPanel} this
22116              * @param {Boolean} state the new state
22117             
22118          */
22119         'changed': true,
22120         /**
22121              * @event beforedeactivate
22122              * Fires before a tab is de-activated - can be used to do validation on a form.
22123              * @param {Roo.bootstrap.TabPanel} this
22124              * @return {Boolean} false if there is an error
22125             
22126          */
22127         'beforedeactivate': true
22128      });
22129     
22130     this.tabId = this.tabId || Roo.id();
22131   
22132 };
22133
22134 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22135     
22136     active: false,
22137     html: false,
22138     tabId: false,
22139     navId : false,
22140     href : '',
22141     touchSlide : false,
22142     getAutoCreate : function(){
22143         
22144         
22145         var cfg = {
22146             tag: 'div',
22147             // item is needed for carousel - not sure if it has any effect otherwise
22148             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22149             html: this.html || ''
22150         };
22151         
22152         if(this.active){
22153             cfg.cls += ' active';
22154         }
22155         
22156         if(this.tabId){
22157             cfg.tabId = this.tabId;
22158         }
22159         
22160         
22161         
22162         return cfg;
22163     },
22164     
22165     initEvents:  function()
22166     {
22167         var p = this.parent();
22168         
22169         this.navId = this.navId || p.navId;
22170         
22171         if (typeof(this.navId) != 'undefined') {
22172             // not really needed.. but just in case.. parent should be a NavGroup.
22173             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22174             
22175             tg.register(this);
22176             
22177             var i = tg.tabs.length - 1;
22178             
22179             if(this.active && tg.bullets > 0 && i < tg.bullets){
22180                 tg.setActiveBullet(i);
22181             }
22182         }
22183         
22184         this.el.on('click', this.onClick, this);
22185         
22186         if(Roo.isTouch && this.touchSlide){
22187             this.el.on("touchstart", this.onTouchStart, this);
22188             this.el.on("touchmove", this.onTouchMove, this);
22189             this.el.on("touchend", this.onTouchEnd, this);
22190         }
22191         
22192     },
22193     
22194     onRender : function(ct, position)
22195     {
22196         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22197     },
22198     
22199     setActive : function(state)
22200     {
22201         Roo.log("panel - set active " + this.tabId + "=" + state);
22202         
22203         this.active = state;
22204         if (!state) {
22205             this.el.removeClass('active');
22206             
22207         } else  if (!this.el.hasClass('active')) {
22208             this.el.addClass('active');
22209         }
22210         
22211         this.fireEvent('changed', this, state);
22212     },
22213     
22214     onClick : function(e)
22215     {
22216         e.preventDefault();
22217         
22218         if(!this.href.length){
22219             return;
22220         }
22221         
22222         window.location.href = this.href;
22223     },
22224     
22225     startX : 0,
22226     startY : 0,
22227     endX : 0,
22228     endY : 0,
22229     swiping : false,
22230     
22231     onTouchStart : function(e)
22232     {
22233         this.swiping = false;
22234         
22235         this.startX = e.browserEvent.touches[0].clientX;
22236         this.startY = e.browserEvent.touches[0].clientY;
22237     },
22238     
22239     onTouchMove : function(e)
22240     {
22241         this.swiping = true;
22242         
22243         this.endX = e.browserEvent.touches[0].clientX;
22244         this.endY = e.browserEvent.touches[0].clientY;
22245     },
22246     
22247     onTouchEnd : function(e)
22248     {
22249         if(!this.swiping){
22250             this.onClick(e);
22251             return;
22252         }
22253         
22254         var tabGroup = this.parent();
22255         
22256         if(this.endX > this.startX){ // swiping right
22257             tabGroup.showPanelPrev();
22258             return;
22259         }
22260         
22261         if(this.startX > this.endX){ // swiping left
22262             tabGroup.showPanelNext();
22263             return;
22264         }
22265     }
22266     
22267     
22268 });
22269  
22270
22271  
22272
22273  /*
22274  * - LGPL
22275  *
22276  * DateField
22277  * 
22278  */
22279
22280 /**
22281  * @class Roo.bootstrap.DateField
22282  * @extends Roo.bootstrap.Input
22283  * Bootstrap DateField class
22284  * @cfg {Number} weekStart default 0
22285  * @cfg {String} viewMode default empty, (months|years)
22286  * @cfg {String} minViewMode default empty, (months|years)
22287  * @cfg {Number} startDate default -Infinity
22288  * @cfg {Number} endDate default Infinity
22289  * @cfg {Boolean} todayHighlight default false
22290  * @cfg {Boolean} todayBtn default false
22291  * @cfg {Boolean} calendarWeeks default false
22292  * @cfg {Object} daysOfWeekDisabled default empty
22293  * @cfg {Boolean} singleMode default false (true | false)
22294  * 
22295  * @cfg {Boolean} keyboardNavigation default true
22296  * @cfg {String} language default en
22297  * 
22298  * @constructor
22299  * Create a new DateField
22300  * @param {Object} config The config object
22301  */
22302
22303 Roo.bootstrap.DateField = function(config){
22304     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22305      this.addEvents({
22306             /**
22307              * @event show
22308              * Fires when this field show.
22309              * @param {Roo.bootstrap.DateField} this
22310              * @param {Mixed} date The date value
22311              */
22312             show : true,
22313             /**
22314              * @event show
22315              * Fires when this field hide.
22316              * @param {Roo.bootstrap.DateField} this
22317              * @param {Mixed} date The date value
22318              */
22319             hide : true,
22320             /**
22321              * @event select
22322              * Fires when select a date.
22323              * @param {Roo.bootstrap.DateField} this
22324              * @param {Mixed} date The date value
22325              */
22326             select : true,
22327             /**
22328              * @event beforeselect
22329              * Fires when before select a date.
22330              * @param {Roo.bootstrap.DateField} this
22331              * @param {Mixed} date The date value
22332              */
22333             beforeselect : true
22334         });
22335 };
22336
22337 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22338     
22339     /**
22340      * @cfg {String} format
22341      * The default date format string which can be overriden for localization support.  The format must be
22342      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22343      */
22344     format : "m/d/y",
22345     /**
22346      * @cfg {String} altFormats
22347      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22348      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22349      */
22350     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22351     
22352     weekStart : 0,
22353     
22354     viewMode : '',
22355     
22356     minViewMode : '',
22357     
22358     todayHighlight : false,
22359     
22360     todayBtn: false,
22361     
22362     language: 'en',
22363     
22364     keyboardNavigation: true,
22365     
22366     calendarWeeks: false,
22367     
22368     startDate: -Infinity,
22369     
22370     endDate: Infinity,
22371     
22372     daysOfWeekDisabled: [],
22373     
22374     _events: [],
22375     
22376     singleMode : false,
22377     
22378     UTCDate: function()
22379     {
22380         return new Date(Date.UTC.apply(Date, arguments));
22381     },
22382     
22383     UTCToday: function()
22384     {
22385         var today = new Date();
22386         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22387     },
22388     
22389     getDate: function() {
22390             var d = this.getUTCDate();
22391             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22392     },
22393     
22394     getUTCDate: function() {
22395             return this.date;
22396     },
22397     
22398     setDate: function(d) {
22399             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22400     },
22401     
22402     setUTCDate: function(d) {
22403             this.date = d;
22404             this.setValue(this.formatDate(this.date));
22405     },
22406         
22407     onRender: function(ct, position)
22408     {
22409         
22410         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22411         
22412         this.language = this.language || 'en';
22413         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22414         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22415         
22416         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22417         this.format = this.format || 'm/d/y';
22418         this.isInline = false;
22419         this.isInput = true;
22420         this.component = this.el.select('.add-on', true).first() || false;
22421         this.component = (this.component && this.component.length === 0) ? false : this.component;
22422         this.hasInput = this.component && this.inputEl().length;
22423         
22424         if (typeof(this.minViewMode === 'string')) {
22425             switch (this.minViewMode) {
22426                 case 'months':
22427                     this.minViewMode = 1;
22428                     break;
22429                 case 'years':
22430                     this.minViewMode = 2;
22431                     break;
22432                 default:
22433                     this.minViewMode = 0;
22434                     break;
22435             }
22436         }
22437         
22438         if (typeof(this.viewMode === 'string')) {
22439             switch (this.viewMode) {
22440                 case 'months':
22441                     this.viewMode = 1;
22442                     break;
22443                 case 'years':
22444                     this.viewMode = 2;
22445                     break;
22446                 default:
22447                     this.viewMode = 0;
22448                     break;
22449             }
22450         }
22451                 
22452         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22453         
22454 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22455         
22456         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22457         
22458         this.picker().on('mousedown', this.onMousedown, this);
22459         this.picker().on('click', this.onClick, this);
22460         
22461         this.picker().addClass('datepicker-dropdown');
22462         
22463         this.startViewMode = this.viewMode;
22464         
22465         if(this.singleMode){
22466             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22467                 v.setVisibilityMode(Roo.Element.DISPLAY);
22468                 v.hide();
22469             });
22470             
22471             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22472                 v.setStyle('width', '189px');
22473             });
22474         }
22475         
22476         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22477             if(!this.calendarWeeks){
22478                 v.remove();
22479                 return;
22480             }
22481             
22482             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22483             v.attr('colspan', function(i, val){
22484                 return parseInt(val) + 1;
22485             });
22486         });
22487                         
22488         
22489         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22490         
22491         this.setStartDate(this.startDate);
22492         this.setEndDate(this.endDate);
22493         
22494         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22495         
22496         this.fillDow();
22497         this.fillMonths();
22498         this.update();
22499         this.showMode();
22500         
22501         if(this.isInline) {
22502             this.showPopup();
22503         }
22504     },
22505     
22506     picker : function()
22507     {
22508         return this.pickerEl;
22509 //        return this.el.select('.datepicker', true).first();
22510     },
22511     
22512     fillDow: function()
22513     {
22514         var dowCnt = this.weekStart;
22515         
22516         var dow = {
22517             tag: 'tr',
22518             cn: [
22519                 
22520             ]
22521         };
22522         
22523         if(this.calendarWeeks){
22524             dow.cn.push({
22525                 tag: 'th',
22526                 cls: 'cw',
22527                 html: '&nbsp;'
22528             })
22529         }
22530         
22531         while (dowCnt < this.weekStart + 7) {
22532             dow.cn.push({
22533                 tag: 'th',
22534                 cls: 'dow',
22535                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22536             });
22537         }
22538         
22539         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22540     },
22541     
22542     fillMonths: function()
22543     {    
22544         var i = 0;
22545         var months = this.picker().select('>.datepicker-months td', true).first();
22546         
22547         months.dom.innerHTML = '';
22548         
22549         while (i < 12) {
22550             var month = {
22551                 tag: 'span',
22552                 cls: 'month',
22553                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22554             };
22555             
22556             months.createChild(month);
22557         }
22558         
22559     },
22560     
22561     update: function()
22562     {
22563         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;
22564         
22565         if (this.date < this.startDate) {
22566             this.viewDate = new Date(this.startDate);
22567         } else if (this.date > this.endDate) {
22568             this.viewDate = new Date(this.endDate);
22569         } else {
22570             this.viewDate = new Date(this.date);
22571         }
22572         
22573         this.fill();
22574     },
22575     
22576     fill: function() 
22577     {
22578         var d = new Date(this.viewDate),
22579                 year = d.getUTCFullYear(),
22580                 month = d.getUTCMonth(),
22581                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22582                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22583                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22584                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22585                 currentDate = this.date && this.date.valueOf(),
22586                 today = this.UTCToday();
22587         
22588         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22589         
22590 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22591         
22592 //        this.picker.select('>tfoot th.today').
22593 //                                              .text(dates[this.language].today)
22594 //                                              .toggle(this.todayBtn !== false);
22595     
22596         this.updateNavArrows();
22597         this.fillMonths();
22598                                                 
22599         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22600         
22601         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22602          
22603         prevMonth.setUTCDate(day);
22604         
22605         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22606         
22607         var nextMonth = new Date(prevMonth);
22608         
22609         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22610         
22611         nextMonth = nextMonth.valueOf();
22612         
22613         var fillMonths = false;
22614         
22615         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22616         
22617         while(prevMonth.valueOf() <= nextMonth) {
22618             var clsName = '';
22619             
22620             if (prevMonth.getUTCDay() === this.weekStart) {
22621                 if(fillMonths){
22622                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22623                 }
22624                     
22625                 fillMonths = {
22626                     tag: 'tr',
22627                     cn: []
22628                 };
22629                 
22630                 if(this.calendarWeeks){
22631                     // ISO 8601: First week contains first thursday.
22632                     // ISO also states week starts on Monday, but we can be more abstract here.
22633                     var
22634                     // Start of current week: based on weekstart/current date
22635                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22636                     // Thursday of this week
22637                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22638                     // First Thursday of year, year from thursday
22639                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22640                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22641                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22642                     
22643                     fillMonths.cn.push({
22644                         tag: 'td',
22645                         cls: 'cw',
22646                         html: calWeek
22647                     });
22648                 }
22649             }
22650             
22651             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22652                 clsName += ' old';
22653             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22654                 clsName += ' new';
22655             }
22656             if (this.todayHighlight &&
22657                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22658                 prevMonth.getUTCMonth() == today.getMonth() &&
22659                 prevMonth.getUTCDate() == today.getDate()) {
22660                 clsName += ' today';
22661             }
22662             
22663             if (currentDate && prevMonth.valueOf() === currentDate) {
22664                 clsName += ' active';
22665             }
22666             
22667             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22668                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22669                     clsName += ' disabled';
22670             }
22671             
22672             fillMonths.cn.push({
22673                 tag: 'td',
22674                 cls: 'day ' + clsName,
22675                 html: prevMonth.getDate()
22676             });
22677             
22678             prevMonth.setDate(prevMonth.getDate()+1);
22679         }
22680           
22681         var currentYear = this.date && this.date.getUTCFullYear();
22682         var currentMonth = this.date && this.date.getUTCMonth();
22683         
22684         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22685         
22686         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22687             v.removeClass('active');
22688             
22689             if(currentYear === year && k === currentMonth){
22690                 v.addClass('active');
22691             }
22692             
22693             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22694                 v.addClass('disabled');
22695             }
22696             
22697         });
22698         
22699         
22700         year = parseInt(year/10, 10) * 10;
22701         
22702         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22703         
22704         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22705         
22706         year -= 1;
22707         for (var i = -1; i < 11; i++) {
22708             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22709                 tag: 'span',
22710                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22711                 html: year
22712             });
22713             
22714             year += 1;
22715         }
22716     },
22717     
22718     showMode: function(dir) 
22719     {
22720         if (dir) {
22721             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22722         }
22723         
22724         Roo.each(this.picker().select('>div',true).elements, function(v){
22725             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22726             v.hide();
22727         });
22728         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22729     },
22730     
22731     place: function()
22732     {
22733         if(this.isInline) {
22734             return;
22735         }
22736         
22737         this.picker().removeClass(['bottom', 'top']);
22738         
22739         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22740             /*
22741              * place to the top of element!
22742              *
22743              */
22744             
22745             this.picker().addClass('top');
22746             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22747             
22748             return;
22749         }
22750         
22751         this.picker().addClass('bottom');
22752         
22753         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22754     },
22755     
22756     parseDate : function(value)
22757     {
22758         if(!value || value instanceof Date){
22759             return value;
22760         }
22761         var v = Date.parseDate(value, this.format);
22762         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22763             v = Date.parseDate(value, 'Y-m-d');
22764         }
22765         if(!v && this.altFormats){
22766             if(!this.altFormatsArray){
22767                 this.altFormatsArray = this.altFormats.split("|");
22768             }
22769             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22770                 v = Date.parseDate(value, this.altFormatsArray[i]);
22771             }
22772         }
22773         return v;
22774     },
22775     
22776     formatDate : function(date, fmt)
22777     {   
22778         return (!date || !(date instanceof Date)) ?
22779         date : date.dateFormat(fmt || this.format);
22780     },
22781     
22782     onFocus : function()
22783     {
22784         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22785         this.showPopup();
22786     },
22787     
22788     onBlur : function()
22789     {
22790         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22791         
22792         var d = this.inputEl().getValue();
22793         
22794         this.setValue(d);
22795                 
22796         this.hidePopup();
22797     },
22798     
22799     showPopup : function()
22800     {
22801         this.picker().show();
22802         this.update();
22803         this.place();
22804         
22805         this.fireEvent('showpopup', this, this.date);
22806     },
22807     
22808     hidePopup : function()
22809     {
22810         if(this.isInline) {
22811             return;
22812         }
22813         this.picker().hide();
22814         this.viewMode = this.startViewMode;
22815         this.showMode();
22816         
22817         this.fireEvent('hidepopup', this, this.date);
22818         
22819     },
22820     
22821     onMousedown: function(e)
22822     {
22823         e.stopPropagation();
22824         e.preventDefault();
22825     },
22826     
22827     keyup: function(e)
22828     {
22829         Roo.bootstrap.DateField.superclass.keyup.call(this);
22830         this.update();
22831     },
22832
22833     setValue: function(v)
22834     {
22835         if(this.fireEvent('beforeselect', this, v) !== false){
22836             var d = new Date(this.parseDate(v) ).clearTime();
22837         
22838             if(isNaN(d.getTime())){
22839                 this.date = this.viewDate = '';
22840                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22841                 return;
22842             }
22843
22844             v = this.formatDate(d);
22845
22846             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22847
22848             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22849
22850             this.update();
22851
22852             this.fireEvent('select', this, this.date);
22853         }
22854     },
22855     
22856     getValue: function()
22857     {
22858         return this.formatDate(this.date);
22859     },
22860     
22861     fireKey: function(e)
22862     {
22863         if (!this.picker().isVisible()){
22864             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22865                 this.showPopup();
22866             }
22867             return;
22868         }
22869         
22870         var dateChanged = false,
22871         dir, day, month,
22872         newDate, newViewDate;
22873         
22874         switch(e.keyCode){
22875             case 27: // escape
22876                 this.hidePopup();
22877                 e.preventDefault();
22878                 break;
22879             case 37: // left
22880             case 39: // right
22881                 if (!this.keyboardNavigation) {
22882                     break;
22883                 }
22884                 dir = e.keyCode == 37 ? -1 : 1;
22885                 
22886                 if (e.ctrlKey){
22887                     newDate = this.moveYear(this.date, dir);
22888                     newViewDate = this.moveYear(this.viewDate, dir);
22889                 } else if (e.shiftKey){
22890                     newDate = this.moveMonth(this.date, dir);
22891                     newViewDate = this.moveMonth(this.viewDate, dir);
22892                 } else {
22893                     newDate = new Date(this.date);
22894                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22895                     newViewDate = new Date(this.viewDate);
22896                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22897                 }
22898                 if (this.dateWithinRange(newDate)){
22899                     this.date = newDate;
22900                     this.viewDate = newViewDate;
22901                     this.setValue(this.formatDate(this.date));
22902 //                    this.update();
22903                     e.preventDefault();
22904                     dateChanged = true;
22905                 }
22906                 break;
22907             case 38: // up
22908             case 40: // down
22909                 if (!this.keyboardNavigation) {
22910                     break;
22911                 }
22912                 dir = e.keyCode == 38 ? -1 : 1;
22913                 if (e.ctrlKey){
22914                     newDate = this.moveYear(this.date, dir);
22915                     newViewDate = this.moveYear(this.viewDate, dir);
22916                 } else if (e.shiftKey){
22917                     newDate = this.moveMonth(this.date, dir);
22918                     newViewDate = this.moveMonth(this.viewDate, dir);
22919                 } else {
22920                     newDate = new Date(this.date);
22921                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22922                     newViewDate = new Date(this.viewDate);
22923                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22924                 }
22925                 if (this.dateWithinRange(newDate)){
22926                     this.date = newDate;
22927                     this.viewDate = newViewDate;
22928                     this.setValue(this.formatDate(this.date));
22929 //                    this.update();
22930                     e.preventDefault();
22931                     dateChanged = true;
22932                 }
22933                 break;
22934             case 13: // enter
22935                 this.setValue(this.formatDate(this.date));
22936                 this.hidePopup();
22937                 e.preventDefault();
22938                 break;
22939             case 9: // tab
22940                 this.setValue(this.formatDate(this.date));
22941                 this.hidePopup();
22942                 break;
22943             case 16: // shift
22944             case 17: // ctrl
22945             case 18: // alt
22946                 break;
22947             default :
22948                 this.hidePopup();
22949                 
22950         }
22951     },
22952     
22953     
22954     onClick: function(e) 
22955     {
22956         e.stopPropagation();
22957         e.preventDefault();
22958         
22959         var target = e.getTarget();
22960         
22961         if(target.nodeName.toLowerCase() === 'i'){
22962             target = Roo.get(target).dom.parentNode;
22963         }
22964         
22965         var nodeName = target.nodeName;
22966         var className = target.className;
22967         var html = target.innerHTML;
22968         //Roo.log(nodeName);
22969         
22970         switch(nodeName.toLowerCase()) {
22971             case 'th':
22972                 switch(className) {
22973                     case 'switch':
22974                         this.showMode(1);
22975                         break;
22976                     case 'prev':
22977                     case 'next':
22978                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22979                         switch(this.viewMode){
22980                                 case 0:
22981                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22982                                         break;
22983                                 case 1:
22984                                 case 2:
22985                                         this.viewDate = this.moveYear(this.viewDate, dir);
22986                                         break;
22987                         }
22988                         this.fill();
22989                         break;
22990                     case 'today':
22991                         var date = new Date();
22992                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22993 //                        this.fill()
22994                         this.setValue(this.formatDate(this.date));
22995                         
22996                         this.hidePopup();
22997                         break;
22998                 }
22999                 break;
23000             case 'span':
23001                 if (className.indexOf('disabled') < 0) {
23002                 if (!this.viewDate) {
23003                     this.viewDate = new Date();
23004                 }
23005                 this.viewDate.setUTCDate(1);
23006                     if (className.indexOf('month') > -1) {
23007                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23008                     } else {
23009                         var year = parseInt(html, 10) || 0;
23010                         this.viewDate.setUTCFullYear(year);
23011                         
23012                     }
23013                     
23014                     if(this.singleMode){
23015                         this.setValue(this.formatDate(this.viewDate));
23016                         this.hidePopup();
23017                         return;
23018                     }
23019                     
23020                     this.showMode(-1);
23021                     this.fill();
23022                 }
23023                 break;
23024                 
23025             case 'td':
23026                 //Roo.log(className);
23027                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23028                     var day = parseInt(html, 10) || 1;
23029                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23030                         month = (this.viewDate || new Date()).getUTCMonth();
23031
23032                     if (className.indexOf('old') > -1) {
23033                         if(month === 0 ){
23034                             month = 11;
23035                             year -= 1;
23036                         }else{
23037                             month -= 1;
23038                         }
23039                     } else if (className.indexOf('new') > -1) {
23040                         if (month == 11) {
23041                             month = 0;
23042                             year += 1;
23043                         } else {
23044                             month += 1;
23045                         }
23046                     }
23047                     //Roo.log([year,month,day]);
23048                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23049                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23050 //                    this.fill();
23051                     //Roo.log(this.formatDate(this.date));
23052                     this.setValue(this.formatDate(this.date));
23053                     this.hidePopup();
23054                 }
23055                 break;
23056         }
23057     },
23058     
23059     setStartDate: function(startDate)
23060     {
23061         this.startDate = startDate || -Infinity;
23062         if (this.startDate !== -Infinity) {
23063             this.startDate = this.parseDate(this.startDate);
23064         }
23065         this.update();
23066         this.updateNavArrows();
23067     },
23068
23069     setEndDate: function(endDate)
23070     {
23071         this.endDate = endDate || Infinity;
23072         if (this.endDate !== Infinity) {
23073             this.endDate = this.parseDate(this.endDate);
23074         }
23075         this.update();
23076         this.updateNavArrows();
23077     },
23078     
23079     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23080     {
23081         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23082         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23083             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23084         }
23085         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23086             return parseInt(d, 10);
23087         });
23088         this.update();
23089         this.updateNavArrows();
23090     },
23091     
23092     updateNavArrows: function() 
23093     {
23094         if(this.singleMode){
23095             return;
23096         }
23097         
23098         var d = new Date(this.viewDate),
23099         year = d.getUTCFullYear(),
23100         month = d.getUTCMonth();
23101         
23102         Roo.each(this.picker().select('.prev', true).elements, function(v){
23103             v.show();
23104             switch (this.viewMode) {
23105                 case 0:
23106
23107                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23108                         v.hide();
23109                     }
23110                     break;
23111                 case 1:
23112                 case 2:
23113                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23114                         v.hide();
23115                     }
23116                     break;
23117             }
23118         });
23119         
23120         Roo.each(this.picker().select('.next', true).elements, function(v){
23121             v.show();
23122             switch (this.viewMode) {
23123                 case 0:
23124
23125                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23126                         v.hide();
23127                     }
23128                     break;
23129                 case 1:
23130                 case 2:
23131                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23132                         v.hide();
23133                     }
23134                     break;
23135             }
23136         })
23137     },
23138     
23139     moveMonth: function(date, dir)
23140     {
23141         if (!dir) {
23142             return date;
23143         }
23144         var new_date = new Date(date.valueOf()),
23145         day = new_date.getUTCDate(),
23146         month = new_date.getUTCMonth(),
23147         mag = Math.abs(dir),
23148         new_month, test;
23149         dir = dir > 0 ? 1 : -1;
23150         if (mag == 1){
23151             test = dir == -1
23152             // If going back one month, make sure month is not current month
23153             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23154             ? function(){
23155                 return new_date.getUTCMonth() == month;
23156             }
23157             // If going forward one month, make sure month is as expected
23158             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23159             : function(){
23160                 return new_date.getUTCMonth() != new_month;
23161             };
23162             new_month = month + dir;
23163             new_date.setUTCMonth(new_month);
23164             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23165             if (new_month < 0 || new_month > 11) {
23166                 new_month = (new_month + 12) % 12;
23167             }
23168         } else {
23169             // For magnitudes >1, move one month at a time...
23170             for (var i=0; i<mag; i++) {
23171                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23172                 new_date = this.moveMonth(new_date, dir);
23173             }
23174             // ...then reset the day, keeping it in the new month
23175             new_month = new_date.getUTCMonth();
23176             new_date.setUTCDate(day);
23177             test = function(){
23178                 return new_month != new_date.getUTCMonth();
23179             };
23180         }
23181         // Common date-resetting loop -- if date is beyond end of month, make it
23182         // end of month
23183         while (test()){
23184             new_date.setUTCDate(--day);
23185             new_date.setUTCMonth(new_month);
23186         }
23187         return new_date;
23188     },
23189
23190     moveYear: function(date, dir)
23191     {
23192         return this.moveMonth(date, dir*12);
23193     },
23194
23195     dateWithinRange: function(date)
23196     {
23197         return date >= this.startDate && date <= this.endDate;
23198     },
23199
23200     
23201     remove: function() 
23202     {
23203         this.picker().remove();
23204     },
23205     
23206     validateValue : function(value)
23207     {
23208         if(this.getVisibilityEl().hasClass('hidden')){
23209             return true;
23210         }
23211         
23212         if(value.length < 1)  {
23213             if(this.allowBlank){
23214                 return true;
23215             }
23216             return false;
23217         }
23218         
23219         if(value.length < this.minLength){
23220             return false;
23221         }
23222         if(value.length > this.maxLength){
23223             return false;
23224         }
23225         if(this.vtype){
23226             var vt = Roo.form.VTypes;
23227             if(!vt[this.vtype](value, this)){
23228                 return false;
23229             }
23230         }
23231         if(typeof this.validator == "function"){
23232             var msg = this.validator(value);
23233             if(msg !== true){
23234                 return false;
23235             }
23236         }
23237         
23238         if(this.regex && !this.regex.test(value)){
23239             return false;
23240         }
23241         
23242         if(typeof(this.parseDate(value)) == 'undefined'){
23243             return false;
23244         }
23245         
23246         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23247             return false;
23248         }      
23249         
23250         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23251             return false;
23252         } 
23253         
23254         
23255         return true;
23256     },
23257     
23258     reset : function()
23259     {
23260         this.date = this.viewDate = '';
23261         
23262         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23263     }
23264    
23265 });
23266
23267 Roo.apply(Roo.bootstrap.DateField,  {
23268     
23269     head : {
23270         tag: 'thead',
23271         cn: [
23272         {
23273             tag: 'tr',
23274             cn: [
23275             {
23276                 tag: 'th',
23277                 cls: 'prev',
23278                 html: '<i class="fa fa-arrow-left"/>'
23279             },
23280             {
23281                 tag: 'th',
23282                 cls: 'switch',
23283                 colspan: '5'
23284             },
23285             {
23286                 tag: 'th',
23287                 cls: 'next',
23288                 html: '<i class="fa fa-arrow-right"/>'
23289             }
23290
23291             ]
23292         }
23293         ]
23294     },
23295     
23296     content : {
23297         tag: 'tbody',
23298         cn: [
23299         {
23300             tag: 'tr',
23301             cn: [
23302             {
23303                 tag: 'td',
23304                 colspan: '7'
23305             }
23306             ]
23307         }
23308         ]
23309     },
23310     
23311     footer : {
23312         tag: 'tfoot',
23313         cn: [
23314         {
23315             tag: 'tr',
23316             cn: [
23317             {
23318                 tag: 'th',
23319                 colspan: '7',
23320                 cls: 'today'
23321             }
23322                     
23323             ]
23324         }
23325         ]
23326     },
23327     
23328     dates:{
23329         en: {
23330             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23331             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23332             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23333             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23334             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23335             today: "Today"
23336         }
23337     },
23338     
23339     modes: [
23340     {
23341         clsName: 'days',
23342         navFnc: 'Month',
23343         navStep: 1
23344     },
23345     {
23346         clsName: 'months',
23347         navFnc: 'FullYear',
23348         navStep: 1
23349     },
23350     {
23351         clsName: 'years',
23352         navFnc: 'FullYear',
23353         navStep: 10
23354     }]
23355 });
23356
23357 Roo.apply(Roo.bootstrap.DateField,  {
23358   
23359     template : {
23360         tag: 'div',
23361         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23362         cn: [
23363         {
23364             tag: 'div',
23365             cls: 'datepicker-days',
23366             cn: [
23367             {
23368                 tag: 'table',
23369                 cls: 'table-condensed',
23370                 cn:[
23371                 Roo.bootstrap.DateField.head,
23372                 {
23373                     tag: 'tbody'
23374                 },
23375                 Roo.bootstrap.DateField.footer
23376                 ]
23377             }
23378             ]
23379         },
23380         {
23381             tag: 'div',
23382             cls: 'datepicker-months',
23383             cn: [
23384             {
23385                 tag: 'table',
23386                 cls: 'table-condensed',
23387                 cn:[
23388                 Roo.bootstrap.DateField.head,
23389                 Roo.bootstrap.DateField.content,
23390                 Roo.bootstrap.DateField.footer
23391                 ]
23392             }
23393             ]
23394         },
23395         {
23396             tag: 'div',
23397             cls: 'datepicker-years',
23398             cn: [
23399             {
23400                 tag: 'table',
23401                 cls: 'table-condensed',
23402                 cn:[
23403                 Roo.bootstrap.DateField.head,
23404                 Roo.bootstrap.DateField.content,
23405                 Roo.bootstrap.DateField.footer
23406                 ]
23407             }
23408             ]
23409         }
23410         ]
23411     }
23412 });
23413
23414  
23415
23416  /*
23417  * - LGPL
23418  *
23419  * TimeField
23420  * 
23421  */
23422
23423 /**
23424  * @class Roo.bootstrap.TimeField
23425  * @extends Roo.bootstrap.Input
23426  * Bootstrap DateField class
23427  * 
23428  * 
23429  * @constructor
23430  * Create a new TimeField
23431  * @param {Object} config The config object
23432  */
23433
23434 Roo.bootstrap.TimeField = function(config){
23435     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23436     this.addEvents({
23437             /**
23438              * @event show
23439              * Fires when this field show.
23440              * @param {Roo.bootstrap.DateField} thisthis
23441              * @param {Mixed} date The date value
23442              */
23443             show : true,
23444             /**
23445              * @event show
23446              * Fires when this field hide.
23447              * @param {Roo.bootstrap.DateField} this
23448              * @param {Mixed} date The date value
23449              */
23450             hide : true,
23451             /**
23452              * @event select
23453              * Fires when select a date.
23454              * @param {Roo.bootstrap.DateField} this
23455              * @param {Mixed} date The date value
23456              */
23457             select : true
23458         });
23459 };
23460
23461 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23462     
23463     /**
23464      * @cfg {String} format
23465      * The default time format string which can be overriden for localization support.  The format must be
23466      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23467      */
23468     format : "H:i",
23469
23470     getAutoCreate : function()
23471     {
23472         this.after = '<i class="fa far fa-clock"></i>';
23473         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23474         
23475          
23476     },
23477     onRender: function(ct, position)
23478     {
23479         
23480         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23481                 
23482         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23483         
23484         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23485         
23486         this.pop = this.picker().select('>.datepicker-time',true).first();
23487         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23488         
23489         this.picker().on('mousedown', this.onMousedown, this);
23490         this.picker().on('click', this.onClick, this);
23491         
23492         this.picker().addClass('datepicker-dropdown');
23493     
23494         this.fillTime();
23495         this.update();
23496             
23497         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23498         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23499         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23500         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23501         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23502         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23503
23504     },
23505     
23506     fireKey: function(e){
23507         if (!this.picker().isVisible()){
23508             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23509                 this.show();
23510             }
23511             return;
23512         }
23513
23514         e.preventDefault();
23515         
23516         switch(e.keyCode){
23517             case 27: // escape
23518                 this.hide();
23519                 break;
23520             case 37: // left
23521             case 39: // right
23522                 this.onTogglePeriod();
23523                 break;
23524             case 38: // up
23525                 this.onIncrementMinutes();
23526                 break;
23527             case 40: // down
23528                 this.onDecrementMinutes();
23529                 break;
23530             case 13: // enter
23531             case 9: // tab
23532                 this.setTime();
23533                 break;
23534         }
23535     },
23536     
23537     onClick: function(e) {
23538         e.stopPropagation();
23539         e.preventDefault();
23540     },
23541     
23542     picker : function()
23543     {
23544         return this.pickerEl;
23545     },
23546     
23547     fillTime: function()
23548     {    
23549         var time = this.pop.select('tbody', true).first();
23550         
23551         time.dom.innerHTML = '';
23552         
23553         time.createChild({
23554             tag: 'tr',
23555             cn: [
23556                 {
23557                     tag: 'td',
23558                     cn: [
23559                         {
23560                             tag: 'a',
23561                             href: '#',
23562                             cls: 'btn',
23563                             cn: [
23564                                 {
23565                                     tag: 'i',
23566                                     cls: 'hours-up fa fas fa-chevron-up'
23567                                 }
23568                             ]
23569                         } 
23570                     ]
23571                 },
23572                 {
23573                     tag: 'td',
23574                     cls: 'separator'
23575                 },
23576                 {
23577                     tag: 'td',
23578                     cn: [
23579                         {
23580                             tag: 'a',
23581                             href: '#',
23582                             cls: 'btn',
23583                             cn: [
23584                                 {
23585                                     tag: 'i',
23586                                     cls: 'minutes-up fa fas fa-chevron-up'
23587                                 }
23588                             ]
23589                         }
23590                     ]
23591                 },
23592                 {
23593                     tag: 'td',
23594                     cls: 'separator'
23595                 }
23596             ]
23597         });
23598         
23599         time.createChild({
23600             tag: 'tr',
23601             cn: [
23602                 {
23603                     tag: 'td',
23604                     cn: [
23605                         {
23606                             tag: 'span',
23607                             cls: 'timepicker-hour',
23608                             html: '00'
23609                         }  
23610                     ]
23611                 },
23612                 {
23613                     tag: 'td',
23614                     cls: 'separator',
23615                     html: ':'
23616                 },
23617                 {
23618                     tag: 'td',
23619                     cn: [
23620                         {
23621                             tag: 'span',
23622                             cls: 'timepicker-minute',
23623                             html: '00'
23624                         }  
23625                     ]
23626                 },
23627                 {
23628                     tag: 'td',
23629                     cls: 'separator'
23630                 },
23631                 {
23632                     tag: 'td',
23633                     cn: [
23634                         {
23635                             tag: 'button',
23636                             type: 'button',
23637                             cls: 'btn btn-primary period',
23638                             html: 'AM'
23639                             
23640                         }
23641                     ]
23642                 }
23643             ]
23644         });
23645         
23646         time.createChild({
23647             tag: 'tr',
23648             cn: [
23649                 {
23650                     tag: 'td',
23651                     cn: [
23652                         {
23653                             tag: 'a',
23654                             href: '#',
23655                             cls: 'btn',
23656                             cn: [
23657                                 {
23658                                     tag: 'span',
23659                                     cls: 'hours-down fa fas fa-chevron-down'
23660                                 }
23661                             ]
23662                         }
23663                     ]
23664                 },
23665                 {
23666                     tag: 'td',
23667                     cls: 'separator'
23668                 },
23669                 {
23670                     tag: 'td',
23671                     cn: [
23672                         {
23673                             tag: 'a',
23674                             href: '#',
23675                             cls: 'btn',
23676                             cn: [
23677                                 {
23678                                     tag: 'span',
23679                                     cls: 'minutes-down fa fas fa-chevron-down'
23680                                 }
23681                             ]
23682                         }
23683                     ]
23684                 },
23685                 {
23686                     tag: 'td',
23687                     cls: 'separator'
23688                 }
23689             ]
23690         });
23691         
23692     },
23693     
23694     update: function()
23695     {
23696         
23697         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23698         
23699         this.fill();
23700     },
23701     
23702     fill: function() 
23703     {
23704         var hours = this.time.getHours();
23705         var minutes = this.time.getMinutes();
23706         var period = 'AM';
23707         
23708         if(hours > 11){
23709             period = 'PM';
23710         }
23711         
23712         if(hours == 0){
23713             hours = 12;
23714         }
23715         
23716         
23717         if(hours > 12){
23718             hours = hours - 12;
23719         }
23720         
23721         if(hours < 10){
23722             hours = '0' + hours;
23723         }
23724         
23725         if(minutes < 10){
23726             minutes = '0' + minutes;
23727         }
23728         
23729         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23730         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23731         this.pop.select('button', true).first().dom.innerHTML = period;
23732         
23733     },
23734     
23735     place: function()
23736     {   
23737         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23738         
23739         var cls = ['bottom'];
23740         
23741         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23742             cls.pop();
23743             cls.push('top');
23744         }
23745         
23746         cls.push('right');
23747         
23748         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23749             cls.pop();
23750             cls.push('left');
23751         }
23752         //this.picker().setXY(20000,20000);
23753         this.picker().addClass(cls.join('-'));
23754         
23755         var _this = this;
23756         
23757         Roo.each(cls, function(c){
23758             if(c == 'bottom'){
23759                 (function() {
23760                  //  
23761                 }).defer(200);
23762                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23763                 //_this.picker().setTop(_this.inputEl().getHeight());
23764                 return;
23765             }
23766             if(c == 'top'){
23767                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23768                 
23769                 //_this.picker().setTop(0 - _this.picker().getHeight());
23770                 return;
23771             }
23772             /*
23773             if(c == 'left'){
23774                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23775                 return;
23776             }
23777             if(c == 'right'){
23778                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23779                 return;
23780             }
23781             */
23782         });
23783         
23784     },
23785   
23786     onFocus : function()
23787     {
23788         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23789         this.show();
23790     },
23791     
23792     onBlur : function()
23793     {
23794         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23795         this.hide();
23796     },
23797     
23798     show : function()
23799     {
23800         this.picker().show();
23801         this.pop.show();
23802         this.update();
23803         this.place();
23804         
23805         this.fireEvent('show', this, this.date);
23806     },
23807     
23808     hide : function()
23809     {
23810         this.picker().hide();
23811         this.pop.hide();
23812         
23813         this.fireEvent('hide', this, this.date);
23814     },
23815     
23816     setTime : function()
23817     {
23818         this.hide();
23819         this.setValue(this.time.format(this.format));
23820         
23821         this.fireEvent('select', this, this.date);
23822         
23823         
23824     },
23825     
23826     onMousedown: function(e){
23827         e.stopPropagation();
23828         e.preventDefault();
23829     },
23830     
23831     onIncrementHours: function()
23832     {
23833         Roo.log('onIncrementHours');
23834         this.time = this.time.add(Date.HOUR, 1);
23835         this.update();
23836         
23837     },
23838     
23839     onDecrementHours: function()
23840     {
23841         Roo.log('onDecrementHours');
23842         this.time = this.time.add(Date.HOUR, -1);
23843         this.update();
23844     },
23845     
23846     onIncrementMinutes: function()
23847     {
23848         Roo.log('onIncrementMinutes');
23849         this.time = this.time.add(Date.MINUTE, 1);
23850         this.update();
23851     },
23852     
23853     onDecrementMinutes: function()
23854     {
23855         Roo.log('onDecrementMinutes');
23856         this.time = this.time.add(Date.MINUTE, -1);
23857         this.update();
23858     },
23859     
23860     onTogglePeriod: function()
23861     {
23862         Roo.log('onTogglePeriod');
23863         this.time = this.time.add(Date.HOUR, 12);
23864         this.update();
23865     }
23866     
23867    
23868 });
23869  
23870
23871 Roo.apply(Roo.bootstrap.TimeField,  {
23872   
23873     template : {
23874         tag: 'div',
23875         cls: 'datepicker dropdown-menu',
23876         cn: [
23877             {
23878                 tag: 'div',
23879                 cls: 'datepicker-time',
23880                 cn: [
23881                 {
23882                     tag: 'table',
23883                     cls: 'table-condensed',
23884                     cn:[
23885                         {
23886                             tag: 'tbody',
23887                             cn: [
23888                                 {
23889                                     tag: 'tr',
23890                                     cn: [
23891                                     {
23892                                         tag: 'td',
23893                                         colspan: '7'
23894                                     }
23895                                     ]
23896                                 }
23897                             ]
23898                         },
23899                         {
23900                             tag: 'tfoot',
23901                             cn: [
23902                                 {
23903                                     tag: 'tr',
23904                                     cn: [
23905                                     {
23906                                         tag: 'th',
23907                                         colspan: '7',
23908                                         cls: '',
23909                                         cn: [
23910                                             {
23911                                                 tag: 'button',
23912                                                 cls: 'btn btn-info ok',
23913                                                 html: 'OK'
23914                                             }
23915                                         ]
23916                                     }
23917                     
23918                                     ]
23919                                 }
23920                             ]
23921                         }
23922                     ]
23923                 }
23924                 ]
23925             }
23926         ]
23927     }
23928 });
23929
23930  
23931
23932  /*
23933  * - LGPL
23934  *
23935  * MonthField
23936  * 
23937  */
23938
23939 /**
23940  * @class Roo.bootstrap.MonthField
23941  * @extends Roo.bootstrap.Input
23942  * Bootstrap MonthField class
23943  * 
23944  * @cfg {String} language default en
23945  * 
23946  * @constructor
23947  * Create a new MonthField
23948  * @param {Object} config The config object
23949  */
23950
23951 Roo.bootstrap.MonthField = function(config){
23952     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23953     
23954     this.addEvents({
23955         /**
23956          * @event show
23957          * Fires when this field show.
23958          * @param {Roo.bootstrap.MonthField} this
23959          * @param {Mixed} date The date value
23960          */
23961         show : true,
23962         /**
23963          * @event show
23964          * Fires when this field hide.
23965          * @param {Roo.bootstrap.MonthField} this
23966          * @param {Mixed} date The date value
23967          */
23968         hide : true,
23969         /**
23970          * @event select
23971          * Fires when select a date.
23972          * @param {Roo.bootstrap.MonthField} this
23973          * @param {String} oldvalue The old value
23974          * @param {String} newvalue The new value
23975          */
23976         select : true
23977     });
23978 };
23979
23980 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23981     
23982     onRender: function(ct, position)
23983     {
23984         
23985         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23986         
23987         this.language = this.language || 'en';
23988         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23989         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23990         
23991         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23992         this.isInline = false;
23993         this.isInput = true;
23994         this.component = this.el.select('.add-on', true).first() || false;
23995         this.component = (this.component && this.component.length === 0) ? false : this.component;
23996         this.hasInput = this.component && this.inputEL().length;
23997         
23998         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23999         
24000         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24001         
24002         this.picker().on('mousedown', this.onMousedown, this);
24003         this.picker().on('click', this.onClick, this);
24004         
24005         this.picker().addClass('datepicker-dropdown');
24006         
24007         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24008             v.setStyle('width', '189px');
24009         });
24010         
24011         this.fillMonths();
24012         
24013         this.update();
24014         
24015         if(this.isInline) {
24016             this.show();
24017         }
24018         
24019     },
24020     
24021     setValue: function(v, suppressEvent)
24022     {   
24023         var o = this.getValue();
24024         
24025         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24026         
24027         this.update();
24028
24029         if(suppressEvent !== true){
24030             this.fireEvent('select', this, o, v);
24031         }
24032         
24033     },
24034     
24035     getValue: function()
24036     {
24037         return this.value;
24038     },
24039     
24040     onClick: function(e) 
24041     {
24042         e.stopPropagation();
24043         e.preventDefault();
24044         
24045         var target = e.getTarget();
24046         
24047         if(target.nodeName.toLowerCase() === 'i'){
24048             target = Roo.get(target).dom.parentNode;
24049         }
24050         
24051         var nodeName = target.nodeName;
24052         var className = target.className;
24053         var html = target.innerHTML;
24054         
24055         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24056             return;
24057         }
24058         
24059         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24060         
24061         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24062         
24063         this.hide();
24064                         
24065     },
24066     
24067     picker : function()
24068     {
24069         return this.pickerEl;
24070     },
24071     
24072     fillMonths: function()
24073     {    
24074         var i = 0;
24075         var months = this.picker().select('>.datepicker-months td', true).first();
24076         
24077         months.dom.innerHTML = '';
24078         
24079         while (i < 12) {
24080             var month = {
24081                 tag: 'span',
24082                 cls: 'month',
24083                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24084             };
24085             
24086             months.createChild(month);
24087         }
24088         
24089     },
24090     
24091     update: function()
24092     {
24093         var _this = this;
24094         
24095         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24096             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24097         }
24098         
24099         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24100             e.removeClass('active');
24101             
24102             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24103                 e.addClass('active');
24104             }
24105         })
24106     },
24107     
24108     place: function()
24109     {
24110         if(this.isInline) {
24111             return;
24112         }
24113         
24114         this.picker().removeClass(['bottom', 'top']);
24115         
24116         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24117             /*
24118              * place to the top of element!
24119              *
24120              */
24121             
24122             this.picker().addClass('top');
24123             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24124             
24125             return;
24126         }
24127         
24128         this.picker().addClass('bottom');
24129         
24130         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24131     },
24132     
24133     onFocus : function()
24134     {
24135         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24136         this.show();
24137     },
24138     
24139     onBlur : function()
24140     {
24141         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24142         
24143         var d = this.inputEl().getValue();
24144         
24145         this.setValue(d);
24146                 
24147         this.hide();
24148     },
24149     
24150     show : function()
24151     {
24152         this.picker().show();
24153         this.picker().select('>.datepicker-months', true).first().show();
24154         this.update();
24155         this.place();
24156         
24157         this.fireEvent('show', this, this.date);
24158     },
24159     
24160     hide : function()
24161     {
24162         if(this.isInline) {
24163             return;
24164         }
24165         this.picker().hide();
24166         this.fireEvent('hide', this, this.date);
24167         
24168     },
24169     
24170     onMousedown: function(e)
24171     {
24172         e.stopPropagation();
24173         e.preventDefault();
24174     },
24175     
24176     keyup: function(e)
24177     {
24178         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24179         this.update();
24180     },
24181
24182     fireKey: function(e)
24183     {
24184         if (!this.picker().isVisible()){
24185             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24186                 this.show();
24187             }
24188             return;
24189         }
24190         
24191         var dir;
24192         
24193         switch(e.keyCode){
24194             case 27: // escape
24195                 this.hide();
24196                 e.preventDefault();
24197                 break;
24198             case 37: // left
24199             case 39: // right
24200                 dir = e.keyCode == 37 ? -1 : 1;
24201                 
24202                 this.vIndex = this.vIndex + dir;
24203                 
24204                 if(this.vIndex < 0){
24205                     this.vIndex = 0;
24206                 }
24207                 
24208                 if(this.vIndex > 11){
24209                     this.vIndex = 11;
24210                 }
24211                 
24212                 if(isNaN(this.vIndex)){
24213                     this.vIndex = 0;
24214                 }
24215                 
24216                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24217                 
24218                 break;
24219             case 38: // up
24220             case 40: // down
24221                 
24222                 dir = e.keyCode == 38 ? -1 : 1;
24223                 
24224                 this.vIndex = this.vIndex + dir * 4;
24225                 
24226                 if(this.vIndex < 0){
24227                     this.vIndex = 0;
24228                 }
24229                 
24230                 if(this.vIndex > 11){
24231                     this.vIndex = 11;
24232                 }
24233                 
24234                 if(isNaN(this.vIndex)){
24235                     this.vIndex = 0;
24236                 }
24237                 
24238                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24239                 break;
24240                 
24241             case 13: // enter
24242                 
24243                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24244                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24245                 }
24246                 
24247                 this.hide();
24248                 e.preventDefault();
24249                 break;
24250             case 9: // tab
24251                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24252                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24253                 }
24254                 this.hide();
24255                 break;
24256             case 16: // shift
24257             case 17: // ctrl
24258             case 18: // alt
24259                 break;
24260             default :
24261                 this.hide();
24262                 
24263         }
24264     },
24265     
24266     remove: function() 
24267     {
24268         this.picker().remove();
24269     }
24270    
24271 });
24272
24273 Roo.apply(Roo.bootstrap.MonthField,  {
24274     
24275     content : {
24276         tag: 'tbody',
24277         cn: [
24278         {
24279             tag: 'tr',
24280             cn: [
24281             {
24282                 tag: 'td',
24283                 colspan: '7'
24284             }
24285             ]
24286         }
24287         ]
24288     },
24289     
24290     dates:{
24291         en: {
24292             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24293             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24294         }
24295     }
24296 });
24297
24298 Roo.apply(Roo.bootstrap.MonthField,  {
24299   
24300     template : {
24301         tag: 'div',
24302         cls: 'datepicker dropdown-menu roo-dynamic',
24303         cn: [
24304             {
24305                 tag: 'div',
24306                 cls: 'datepicker-months',
24307                 cn: [
24308                 {
24309                     tag: 'table',
24310                     cls: 'table-condensed',
24311                     cn:[
24312                         Roo.bootstrap.DateField.content
24313                     ]
24314                 }
24315                 ]
24316             }
24317         ]
24318     }
24319 });
24320
24321  
24322
24323  
24324  /*
24325  * - LGPL
24326  *
24327  * CheckBox
24328  * 
24329  */
24330
24331 /**
24332  * @class Roo.bootstrap.CheckBox
24333  * @extends Roo.bootstrap.Input
24334  * Bootstrap CheckBox class
24335  * 
24336  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24337  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24338  * @cfg {String} boxLabel The text that appears beside the checkbox
24339  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24340  * @cfg {Boolean} checked initnal the element
24341  * @cfg {Boolean} inline inline the element (default false)
24342  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24343  * @cfg {String} tooltip label tooltip
24344  * 
24345  * @constructor
24346  * Create a new CheckBox
24347  * @param {Object} config The config object
24348  */
24349
24350 Roo.bootstrap.CheckBox = function(config){
24351     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24352    
24353     this.addEvents({
24354         /**
24355         * @event check
24356         * Fires when the element is checked or unchecked.
24357         * @param {Roo.bootstrap.CheckBox} this This input
24358         * @param {Boolean} checked The new checked value
24359         */
24360        check : true,
24361        /**
24362         * @event click
24363         * Fires when the element is click.
24364         * @param {Roo.bootstrap.CheckBox} this This input
24365         */
24366        click : true
24367     });
24368     
24369 };
24370
24371 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24372   
24373     inputType: 'checkbox',
24374     inputValue: 1,
24375     valueOff: 0,
24376     boxLabel: false,
24377     checked: false,
24378     weight : false,
24379     inline: false,
24380     tooltip : '',
24381     
24382     // checkbox success does not make any sense really.. 
24383     invalidClass : "",
24384     validClass : "",
24385     
24386     
24387     getAutoCreate : function()
24388     {
24389         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24390         
24391         var id = Roo.id();
24392         
24393         var cfg = {};
24394         
24395         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24396         
24397         if(this.inline){
24398             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24399         }
24400         
24401         var input =  {
24402             tag: 'input',
24403             id : id,
24404             type : this.inputType,
24405             value : this.inputValue,
24406             cls : 'roo-' + this.inputType, //'form-box',
24407             placeholder : this.placeholder || ''
24408             
24409         };
24410         
24411         if(this.inputType != 'radio'){
24412             var hidden =  {
24413                 tag: 'input',
24414                 type : 'hidden',
24415                 cls : 'roo-hidden-value',
24416                 value : this.checked ? this.inputValue : this.valueOff
24417             };
24418         }
24419         
24420             
24421         if (this.weight) { // Validity check?
24422             cfg.cls += " " + this.inputType + "-" + this.weight;
24423         }
24424         
24425         if (this.disabled) {
24426             input.disabled=true;
24427         }
24428         
24429         if(this.checked){
24430             input.checked = this.checked;
24431         }
24432         
24433         if (this.name) {
24434             
24435             input.name = this.name;
24436             
24437             if(this.inputType != 'radio'){
24438                 hidden.name = this.name;
24439                 input.name = '_hidden_' + this.name;
24440             }
24441         }
24442         
24443         if (this.size) {
24444             input.cls += ' input-' + this.size;
24445         }
24446         
24447         var settings=this;
24448         
24449         ['xs','sm','md','lg'].map(function(size){
24450             if (settings[size]) {
24451                 cfg.cls += ' col-' + size + '-' + settings[size];
24452             }
24453         });
24454         
24455         var inputblock = input;
24456          
24457         if (this.before || this.after) {
24458             
24459             inputblock = {
24460                 cls : 'input-group',
24461                 cn :  [] 
24462             };
24463             
24464             if (this.before) {
24465                 inputblock.cn.push({
24466                     tag :'span',
24467                     cls : 'input-group-addon',
24468                     html : this.before
24469                 });
24470             }
24471             
24472             inputblock.cn.push(input);
24473             
24474             if(this.inputType != 'radio'){
24475                 inputblock.cn.push(hidden);
24476             }
24477             
24478             if (this.after) {
24479                 inputblock.cn.push({
24480                     tag :'span',
24481                     cls : 'input-group-addon',
24482                     html : this.after
24483                 });
24484             }
24485             
24486         }
24487         var boxLabelCfg = false;
24488         
24489         if(this.boxLabel){
24490            
24491             boxLabelCfg = {
24492                 tag: 'label',
24493                 //'for': id, // box label is handled by onclick - so no for...
24494                 cls: 'box-label',
24495                 html: this.boxLabel
24496             };
24497             if(this.tooltip){
24498                 boxLabelCfg.tooltip = this.tooltip;
24499             }
24500              
24501         }
24502         
24503         
24504         if (align ==='left' && this.fieldLabel.length) {
24505 //                Roo.log("left and has label");
24506             cfg.cn = [
24507                 {
24508                     tag: 'label',
24509                     'for' :  id,
24510                     cls : 'control-label',
24511                     html : this.fieldLabel
24512                 },
24513                 {
24514                     cls : "", 
24515                     cn: [
24516                         inputblock
24517                     ]
24518                 }
24519             ];
24520             
24521             if (boxLabelCfg) {
24522                 cfg.cn[1].cn.push(boxLabelCfg);
24523             }
24524             
24525             if(this.labelWidth > 12){
24526                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24527             }
24528             
24529             if(this.labelWidth < 13 && this.labelmd == 0){
24530                 this.labelmd = this.labelWidth;
24531             }
24532             
24533             if(this.labellg > 0){
24534                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24535                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24536             }
24537             
24538             if(this.labelmd > 0){
24539                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24540                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24541             }
24542             
24543             if(this.labelsm > 0){
24544                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24545                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24546             }
24547             
24548             if(this.labelxs > 0){
24549                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24550                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24551             }
24552             
24553         } else if ( this.fieldLabel.length) {
24554 //                Roo.log(" label");
24555                 cfg.cn = [
24556                    
24557                     {
24558                         tag: this.boxLabel ? 'span' : 'label',
24559                         'for': id,
24560                         cls: 'control-label box-input-label',
24561                         //cls : 'input-group-addon',
24562                         html : this.fieldLabel
24563                     },
24564                     
24565                     inputblock
24566                     
24567                 ];
24568                 if (boxLabelCfg) {
24569                     cfg.cn.push(boxLabelCfg);
24570                 }
24571
24572         } else {
24573             
24574 //                Roo.log(" no label && no align");
24575                 cfg.cn = [  inputblock ] ;
24576                 if (boxLabelCfg) {
24577                     cfg.cn.push(boxLabelCfg);
24578                 }
24579
24580                 
24581         }
24582         
24583        
24584         
24585         if(this.inputType != 'radio'){
24586             cfg.cn.push(hidden);
24587         }
24588         
24589         return cfg;
24590         
24591     },
24592     
24593     /**
24594      * return the real input element.
24595      */
24596     inputEl: function ()
24597     {
24598         return this.el.select('input.roo-' + this.inputType,true).first();
24599     },
24600     hiddenEl: function ()
24601     {
24602         return this.el.select('input.roo-hidden-value',true).first();
24603     },
24604     
24605     labelEl: function()
24606     {
24607         return this.el.select('label.control-label',true).first();
24608     },
24609     /* depricated... */
24610     
24611     label: function()
24612     {
24613         return this.labelEl();
24614     },
24615     
24616     boxLabelEl: function()
24617     {
24618         return this.el.select('label.box-label',true).first();
24619     },
24620     
24621     initEvents : function()
24622     {
24623 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24624         
24625         this.inputEl().on('click', this.onClick,  this);
24626         
24627         if (this.boxLabel) { 
24628             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24629         }
24630         
24631         this.startValue = this.getValue();
24632         
24633         if(this.groupId){
24634             Roo.bootstrap.CheckBox.register(this);
24635         }
24636     },
24637     
24638     onClick : function(e)
24639     {   
24640         if(this.fireEvent('click', this, e) !== false){
24641             this.setChecked(!this.checked);
24642         }
24643         
24644     },
24645     
24646     setChecked : function(state,suppressEvent)
24647     {
24648         this.startValue = this.getValue();
24649
24650         if(this.inputType == 'radio'){
24651             
24652             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24653                 e.dom.checked = false;
24654             });
24655             
24656             this.inputEl().dom.checked = true;
24657             
24658             this.inputEl().dom.value = this.inputValue;
24659             
24660             if(suppressEvent !== true){
24661                 this.fireEvent('check', this, true);
24662             }
24663             
24664             this.validate();
24665             
24666             return;
24667         }
24668         
24669         this.checked = state;
24670         
24671         this.inputEl().dom.checked = state;
24672         
24673         
24674         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24675         
24676         if(suppressEvent !== true){
24677             this.fireEvent('check', this, state);
24678         }
24679         
24680         this.validate();
24681     },
24682     
24683     getValue : function()
24684     {
24685         if(this.inputType == 'radio'){
24686             return this.getGroupValue();
24687         }
24688         
24689         return this.hiddenEl().dom.value;
24690         
24691     },
24692     
24693     getGroupValue : function()
24694     {
24695         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24696             return '';
24697         }
24698         
24699         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24700     },
24701     
24702     setValue : function(v,suppressEvent)
24703     {
24704         if(this.inputType == 'radio'){
24705             this.setGroupValue(v, suppressEvent);
24706             return;
24707         }
24708         
24709         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24710         
24711         this.validate();
24712     },
24713     
24714     setGroupValue : function(v, suppressEvent)
24715     {
24716         this.startValue = this.getValue();
24717         
24718         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24719             e.dom.checked = false;
24720             
24721             if(e.dom.value == v){
24722                 e.dom.checked = true;
24723             }
24724         });
24725         
24726         if(suppressEvent !== true){
24727             this.fireEvent('check', this, true);
24728         }
24729
24730         this.validate();
24731         
24732         return;
24733     },
24734     
24735     validate : function()
24736     {
24737         if(this.getVisibilityEl().hasClass('hidden')){
24738             return true;
24739         }
24740         
24741         if(
24742                 this.disabled || 
24743                 (this.inputType == 'radio' && this.validateRadio()) ||
24744                 (this.inputType == 'checkbox' && this.validateCheckbox())
24745         ){
24746             this.markValid();
24747             return true;
24748         }
24749         
24750         this.markInvalid();
24751         return false;
24752     },
24753     
24754     validateRadio : function()
24755     {
24756         if(this.getVisibilityEl().hasClass('hidden')){
24757             return true;
24758         }
24759         
24760         if(this.allowBlank){
24761             return true;
24762         }
24763         
24764         var valid = false;
24765         
24766         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24767             if(!e.dom.checked){
24768                 return;
24769             }
24770             
24771             valid = true;
24772             
24773             return false;
24774         });
24775         
24776         return valid;
24777     },
24778     
24779     validateCheckbox : function()
24780     {
24781         if(!this.groupId){
24782             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24783             //return (this.getValue() == this.inputValue) ? true : false;
24784         }
24785         
24786         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24787         
24788         if(!group){
24789             return false;
24790         }
24791         
24792         var r = false;
24793         
24794         for(var i in group){
24795             if(group[i].el.isVisible(true)){
24796                 r = false;
24797                 break;
24798             }
24799             
24800             r = true;
24801         }
24802         
24803         for(var i in group){
24804             if(r){
24805                 break;
24806             }
24807             
24808             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24809         }
24810         
24811         return r;
24812     },
24813     
24814     /**
24815      * Mark this field as valid
24816      */
24817     markValid : function()
24818     {
24819         var _this = this;
24820         
24821         this.fireEvent('valid', this);
24822         
24823         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24824         
24825         if(this.groupId){
24826             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24827         }
24828         
24829         if(label){
24830             label.markValid();
24831         }
24832
24833         if(this.inputType == 'radio'){
24834             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24835                 var fg = e.findParent('.form-group', false, true);
24836                 if (Roo.bootstrap.version == 3) {
24837                     fg.removeClass([_this.invalidClass, _this.validClass]);
24838                     fg.addClass(_this.validClass);
24839                 } else {
24840                     fg.removeClass(['is-valid', 'is-invalid']);
24841                     fg.addClass('is-valid');
24842                 }
24843             });
24844             
24845             return;
24846         }
24847
24848         if(!this.groupId){
24849             var fg = this.el.findParent('.form-group', false, true);
24850             if (Roo.bootstrap.version == 3) {
24851                 fg.removeClass([this.invalidClass, this.validClass]);
24852                 fg.addClass(this.validClass);
24853             } else {
24854                 fg.removeClass(['is-valid', 'is-invalid']);
24855                 fg.addClass('is-valid');
24856             }
24857             return;
24858         }
24859         
24860         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24861         
24862         if(!group){
24863             return;
24864         }
24865         
24866         for(var i in group){
24867             var fg = group[i].el.findParent('.form-group', false, true);
24868             if (Roo.bootstrap.version == 3) {
24869                 fg.removeClass([this.invalidClass, this.validClass]);
24870                 fg.addClass(this.validClass);
24871             } else {
24872                 fg.removeClass(['is-valid', 'is-invalid']);
24873                 fg.addClass('is-valid');
24874             }
24875         }
24876     },
24877     
24878      /**
24879      * Mark this field as invalid
24880      * @param {String} msg The validation message
24881      */
24882     markInvalid : function(msg)
24883     {
24884         if(this.allowBlank){
24885             return;
24886         }
24887         
24888         var _this = this;
24889         
24890         this.fireEvent('invalid', this, msg);
24891         
24892         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24893         
24894         if(this.groupId){
24895             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24896         }
24897         
24898         if(label){
24899             label.markInvalid();
24900         }
24901             
24902         if(this.inputType == 'radio'){
24903             
24904             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24905                 var fg = e.findParent('.form-group', false, true);
24906                 if (Roo.bootstrap.version == 3) {
24907                     fg.removeClass([_this.invalidClass, _this.validClass]);
24908                     fg.addClass(_this.invalidClass);
24909                 } else {
24910                     fg.removeClass(['is-invalid', 'is-valid']);
24911                     fg.addClass('is-invalid');
24912                 }
24913             });
24914             
24915             return;
24916         }
24917         
24918         if(!this.groupId){
24919             var fg = this.el.findParent('.form-group', false, true);
24920             if (Roo.bootstrap.version == 3) {
24921                 fg.removeClass([_this.invalidClass, _this.validClass]);
24922                 fg.addClass(_this.invalidClass);
24923             } else {
24924                 fg.removeClass(['is-invalid', 'is-valid']);
24925                 fg.addClass('is-invalid');
24926             }
24927             return;
24928         }
24929         
24930         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24931         
24932         if(!group){
24933             return;
24934         }
24935         
24936         for(var i in group){
24937             var fg = group[i].el.findParent('.form-group', false, true);
24938             if (Roo.bootstrap.version == 3) {
24939                 fg.removeClass([_this.invalidClass, _this.validClass]);
24940                 fg.addClass(_this.invalidClass);
24941             } else {
24942                 fg.removeClass(['is-invalid', 'is-valid']);
24943                 fg.addClass('is-invalid');
24944             }
24945         }
24946         
24947     },
24948     
24949     clearInvalid : function()
24950     {
24951         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24952         
24953         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24954         
24955         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24956         
24957         if (label && label.iconEl) {
24958             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24959             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24960         }
24961     },
24962     
24963     disable : function()
24964     {
24965         if(this.inputType != 'radio'){
24966             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24967             return;
24968         }
24969         
24970         var _this = this;
24971         
24972         if(this.rendered){
24973             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24974                 _this.getActionEl().addClass(this.disabledClass);
24975                 e.dom.disabled = true;
24976             });
24977         }
24978         
24979         this.disabled = true;
24980         this.fireEvent("disable", this);
24981         return this;
24982     },
24983
24984     enable : function()
24985     {
24986         if(this.inputType != 'radio'){
24987             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24988             return;
24989         }
24990         
24991         var _this = this;
24992         
24993         if(this.rendered){
24994             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24995                 _this.getActionEl().removeClass(this.disabledClass);
24996                 e.dom.disabled = false;
24997             });
24998         }
24999         
25000         this.disabled = false;
25001         this.fireEvent("enable", this);
25002         return this;
25003     },
25004     
25005     setBoxLabel : function(v)
25006     {
25007         this.boxLabel = v;
25008         
25009         if(this.rendered){
25010             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25011         }
25012     }
25013
25014 });
25015
25016 Roo.apply(Roo.bootstrap.CheckBox, {
25017     
25018     groups: {},
25019     
25020      /**
25021     * register a CheckBox Group
25022     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25023     */
25024     register : function(checkbox)
25025     {
25026         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25027             this.groups[checkbox.groupId] = {};
25028         }
25029         
25030         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25031             return;
25032         }
25033         
25034         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25035         
25036     },
25037     /**
25038     * fetch a CheckBox Group based on the group ID
25039     * @param {string} the group ID
25040     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25041     */
25042     get: function(groupId) {
25043         if (typeof(this.groups[groupId]) == 'undefined') {
25044             return false;
25045         }
25046         
25047         return this.groups[groupId] ;
25048     }
25049     
25050     
25051 });
25052 /*
25053  * - LGPL
25054  *
25055  * RadioItem
25056  * 
25057  */
25058
25059 /**
25060  * @class Roo.bootstrap.Radio
25061  * @extends Roo.bootstrap.Component
25062  * Bootstrap Radio class
25063  * @cfg {String} boxLabel - the label associated
25064  * @cfg {String} value - the value of radio
25065  * 
25066  * @constructor
25067  * Create a new Radio
25068  * @param {Object} config The config object
25069  */
25070 Roo.bootstrap.Radio = function(config){
25071     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25072     
25073 };
25074
25075 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25076     
25077     boxLabel : '',
25078     
25079     value : '',
25080     
25081     getAutoCreate : function()
25082     {
25083         var cfg = {
25084             tag : 'div',
25085             cls : 'form-group radio',
25086             cn : [
25087                 {
25088                     tag : 'label',
25089                     cls : 'box-label',
25090                     html : this.boxLabel
25091                 }
25092             ]
25093         };
25094         
25095         return cfg;
25096     },
25097     
25098     initEvents : function() 
25099     {
25100         this.parent().register(this);
25101         
25102         this.el.on('click', this.onClick, this);
25103         
25104     },
25105     
25106     onClick : function(e)
25107     {
25108         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25109             this.setChecked(true);
25110         }
25111     },
25112     
25113     setChecked : function(state, suppressEvent)
25114     {
25115         this.parent().setValue(this.value, suppressEvent);
25116         
25117     },
25118     
25119     setBoxLabel : function(v)
25120     {
25121         this.boxLabel = v;
25122         
25123         if(this.rendered){
25124             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25125         }
25126     }
25127     
25128 });
25129  
25130
25131  /*
25132  * - LGPL
25133  *
25134  * Input
25135  * 
25136  */
25137
25138 /**
25139  * @class Roo.bootstrap.SecurePass
25140  * @extends Roo.bootstrap.Input
25141  * Bootstrap SecurePass class
25142  *
25143  * 
25144  * @constructor
25145  * Create a new SecurePass
25146  * @param {Object} config The config object
25147  */
25148  
25149 Roo.bootstrap.SecurePass = function (config) {
25150     // these go here, so the translation tool can replace them..
25151     this.errors = {
25152         PwdEmpty: "Please type a password, and then retype it to confirm.",
25153         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25154         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25155         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25156         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25157         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25158         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25159         TooWeak: "Your password is Too Weak."
25160     },
25161     this.meterLabel = "Password strength:";
25162     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25163     this.meterClass = [
25164         "roo-password-meter-tooweak", 
25165         "roo-password-meter-weak", 
25166         "roo-password-meter-medium", 
25167         "roo-password-meter-strong", 
25168         "roo-password-meter-grey"
25169     ];
25170     
25171     this.errors = {};
25172     
25173     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25174 }
25175
25176 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25177     /**
25178      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25179      * {
25180      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25181      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25182      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25183      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25184      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25185      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25186      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25187      * })
25188      */
25189     // private
25190     
25191     meterWidth: 300,
25192     errorMsg :'',    
25193     errors: false,
25194     imageRoot: '/',
25195     /**
25196      * @cfg {String/Object} Label for the strength meter (defaults to
25197      * 'Password strength:')
25198      */
25199     // private
25200     meterLabel: '',
25201     /**
25202      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25203      * ['Weak', 'Medium', 'Strong'])
25204      */
25205     // private    
25206     pwdStrengths: false,    
25207     // private
25208     strength: 0,
25209     // private
25210     _lastPwd: null,
25211     // private
25212     kCapitalLetter: 0,
25213     kSmallLetter: 1,
25214     kDigit: 2,
25215     kPunctuation: 3,
25216     
25217     insecure: false,
25218     // private
25219     initEvents: function ()
25220     {
25221         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25222
25223         if (this.el.is('input[type=password]') && Roo.isSafari) {
25224             this.el.on('keydown', this.SafariOnKeyDown, this);
25225         }
25226
25227         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25228     },
25229     // private
25230     onRender: function (ct, position)
25231     {
25232         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25233         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25234         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25235
25236         this.trigger.createChild({
25237                    cn: [
25238                     {
25239                     //id: 'PwdMeter',
25240                     tag: 'div',
25241                     cls: 'roo-password-meter-grey col-xs-12',
25242                     style: {
25243                         //width: 0,
25244                         //width: this.meterWidth + 'px'                                                
25245                         }
25246                     },
25247                     {                            
25248                          cls: 'roo-password-meter-text'                          
25249                     }
25250                 ]            
25251         });
25252
25253          
25254         if (this.hideTrigger) {
25255             this.trigger.setDisplayed(false);
25256         }
25257         this.setSize(this.width || '', this.height || '');
25258     },
25259     // private
25260     onDestroy: function ()
25261     {
25262         if (this.trigger) {
25263             this.trigger.removeAllListeners();
25264             this.trigger.remove();
25265         }
25266         if (this.wrap) {
25267             this.wrap.remove();
25268         }
25269         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25270     },
25271     // private
25272     checkStrength: function ()
25273     {
25274         var pwd = this.inputEl().getValue();
25275         if (pwd == this._lastPwd) {
25276             return;
25277         }
25278
25279         var strength;
25280         if (this.ClientSideStrongPassword(pwd)) {
25281             strength = 3;
25282         } else if (this.ClientSideMediumPassword(pwd)) {
25283             strength = 2;
25284         } else if (this.ClientSideWeakPassword(pwd)) {
25285             strength = 1;
25286         } else {
25287             strength = 0;
25288         }
25289         
25290         Roo.log('strength1: ' + strength);
25291         
25292         //var pm = this.trigger.child('div/div/div').dom;
25293         var pm = this.trigger.child('div/div');
25294         pm.removeClass(this.meterClass);
25295         pm.addClass(this.meterClass[strength]);
25296                 
25297         
25298         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25299                 
25300         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25301         
25302         this._lastPwd = pwd;
25303     },
25304     reset: function ()
25305     {
25306         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25307         
25308         this._lastPwd = '';
25309         
25310         var pm = this.trigger.child('div/div');
25311         pm.removeClass(this.meterClass);
25312         pm.addClass('roo-password-meter-grey');        
25313         
25314         
25315         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25316         
25317         pt.innerHTML = '';
25318         this.inputEl().dom.type='password';
25319     },
25320     // private
25321     validateValue: function (value)
25322     {
25323         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25324             return false;
25325         }
25326         if (value.length == 0) {
25327             if (this.allowBlank) {
25328                 this.clearInvalid();
25329                 return true;
25330             }
25331
25332             this.markInvalid(this.errors.PwdEmpty);
25333             this.errorMsg = this.errors.PwdEmpty;
25334             return false;
25335         }
25336         
25337         if(this.insecure){
25338             return true;
25339         }
25340         
25341         if (!value.match(/[\x21-\x7e]+/)) {
25342             this.markInvalid(this.errors.PwdBadChar);
25343             this.errorMsg = this.errors.PwdBadChar;
25344             return false;
25345         }
25346         if (value.length < 6) {
25347             this.markInvalid(this.errors.PwdShort);
25348             this.errorMsg = this.errors.PwdShort;
25349             return false;
25350         }
25351         if (value.length > 16) {
25352             this.markInvalid(this.errors.PwdLong);
25353             this.errorMsg = this.errors.PwdLong;
25354             return false;
25355         }
25356         var strength;
25357         if (this.ClientSideStrongPassword(value)) {
25358             strength = 3;
25359         } else if (this.ClientSideMediumPassword(value)) {
25360             strength = 2;
25361         } else if (this.ClientSideWeakPassword(value)) {
25362             strength = 1;
25363         } else {
25364             strength = 0;
25365         }
25366
25367         
25368         if (strength < 2) {
25369             //this.markInvalid(this.errors.TooWeak);
25370             this.errorMsg = this.errors.TooWeak;
25371             //return false;
25372         }
25373         
25374         
25375         console.log('strength2: ' + strength);
25376         
25377         //var pm = this.trigger.child('div/div/div').dom;
25378         
25379         var pm = this.trigger.child('div/div');
25380         pm.removeClass(this.meterClass);
25381         pm.addClass(this.meterClass[strength]);
25382                 
25383         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25384                 
25385         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25386         
25387         this.errorMsg = ''; 
25388         return true;
25389     },
25390     // private
25391     CharacterSetChecks: function (type)
25392     {
25393         this.type = type;
25394         this.fResult = false;
25395     },
25396     // private
25397     isctype: function (character, type)
25398     {
25399         switch (type) {  
25400             case this.kCapitalLetter:
25401                 if (character >= 'A' && character <= 'Z') {
25402                     return true;
25403                 }
25404                 break;
25405             
25406             case this.kSmallLetter:
25407                 if (character >= 'a' && character <= 'z') {
25408                     return true;
25409                 }
25410                 break;
25411             
25412             case this.kDigit:
25413                 if (character >= '0' && character <= '9') {
25414                     return true;
25415                 }
25416                 break;
25417             
25418             case this.kPunctuation:
25419                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25420                     return true;
25421                 }
25422                 break;
25423             
25424             default:
25425                 return false;
25426         }
25427
25428     },
25429     // private
25430     IsLongEnough: function (pwd, size)
25431     {
25432         return !(pwd == null || isNaN(size) || pwd.length < size);
25433     },
25434     // private
25435     SpansEnoughCharacterSets: function (word, nb)
25436     {
25437         if (!this.IsLongEnough(word, nb))
25438         {
25439             return false;
25440         }
25441
25442         var characterSetChecks = new Array(
25443             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25444             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25445         );
25446         
25447         for (var index = 0; index < word.length; ++index) {
25448             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25449                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25450                     characterSetChecks[nCharSet].fResult = true;
25451                     break;
25452                 }
25453             }
25454         }
25455
25456         var nCharSets = 0;
25457         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25458             if (characterSetChecks[nCharSet].fResult) {
25459                 ++nCharSets;
25460             }
25461         }
25462
25463         if (nCharSets < nb) {
25464             return false;
25465         }
25466         return true;
25467     },
25468     // private
25469     ClientSideStrongPassword: function (pwd)
25470     {
25471         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25472     },
25473     // private
25474     ClientSideMediumPassword: function (pwd)
25475     {
25476         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25477     },
25478     // private
25479     ClientSideWeakPassword: function (pwd)
25480     {
25481         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25482     }
25483           
25484 })//<script type="text/javascript">
25485
25486 /*
25487  * Based  Ext JS Library 1.1.1
25488  * Copyright(c) 2006-2007, Ext JS, LLC.
25489  * LGPL
25490  *
25491  */
25492  
25493 /**
25494  * @class Roo.HtmlEditorCore
25495  * @extends Roo.Component
25496  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25497  *
25498  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25499  */
25500
25501 Roo.HtmlEditorCore = function(config){
25502     
25503     
25504     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25505     
25506     
25507     this.addEvents({
25508         /**
25509          * @event initialize
25510          * Fires when the editor is fully initialized (including the iframe)
25511          * @param {Roo.HtmlEditorCore} this
25512          */
25513         initialize: true,
25514         /**
25515          * @event activate
25516          * Fires when the editor is first receives the focus. Any insertion must wait
25517          * until after this event.
25518          * @param {Roo.HtmlEditorCore} this
25519          */
25520         activate: true,
25521          /**
25522          * @event beforesync
25523          * Fires before the textarea is updated with content from the editor iframe. Return false
25524          * to cancel the sync.
25525          * @param {Roo.HtmlEditorCore} this
25526          * @param {String} html
25527          */
25528         beforesync: true,
25529          /**
25530          * @event beforepush
25531          * Fires before the iframe editor is updated with content from the textarea. Return false
25532          * to cancel the push.
25533          * @param {Roo.HtmlEditorCore} this
25534          * @param {String} html
25535          */
25536         beforepush: true,
25537          /**
25538          * @event sync
25539          * Fires when the textarea is updated with content from the editor iframe.
25540          * @param {Roo.HtmlEditorCore} this
25541          * @param {String} html
25542          */
25543         sync: true,
25544          /**
25545          * @event push
25546          * Fires when the iframe editor is updated with content from the textarea.
25547          * @param {Roo.HtmlEditorCore} this
25548          * @param {String} html
25549          */
25550         push: true,
25551         
25552         /**
25553          * @event editorevent
25554          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25555          * @param {Roo.HtmlEditorCore} this
25556          */
25557         editorevent: true
25558         
25559     });
25560     
25561     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25562     
25563     // defaults : white / black...
25564     this.applyBlacklists();
25565     
25566     
25567     
25568 };
25569
25570
25571 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25572
25573
25574      /**
25575      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25576      */
25577     
25578     owner : false,
25579     
25580      /**
25581      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25582      *                        Roo.resizable.
25583      */
25584     resizable : false,
25585      /**
25586      * @cfg {Number} height (in pixels)
25587      */   
25588     height: 300,
25589    /**
25590      * @cfg {Number} width (in pixels)
25591      */   
25592     width: 500,
25593     
25594     /**
25595      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25596      * 
25597      */
25598     stylesheets: false,
25599     
25600     // id of frame..
25601     frameId: false,
25602     
25603     // private properties
25604     validationEvent : false,
25605     deferHeight: true,
25606     initialized : false,
25607     activated : false,
25608     sourceEditMode : false,
25609     onFocus : Roo.emptyFn,
25610     iframePad:3,
25611     hideMode:'offsets',
25612     
25613     clearUp: true,
25614     
25615     // blacklist + whitelisted elements..
25616     black: false,
25617     white: false,
25618      
25619     bodyCls : '',
25620
25621     /**
25622      * Protected method that will not generally be called directly. It
25623      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25624      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25625      */
25626     getDocMarkup : function(){
25627         // body styles..
25628         var st = '';
25629         
25630         // inherit styels from page...?? 
25631         if (this.stylesheets === false) {
25632             
25633             Roo.get(document.head).select('style').each(function(node) {
25634                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25635             });
25636             
25637             Roo.get(document.head).select('link').each(function(node) { 
25638                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25639             });
25640             
25641         } else if (!this.stylesheets.length) {
25642                 // simple..
25643                 st = '<style type="text/css">' +
25644                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25645                    '</style>';
25646         } else {
25647             for (var i in this.stylesheets) { 
25648                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25649             }
25650             
25651         }
25652         
25653         st +=  '<style type="text/css">' +
25654             'IMG { cursor: pointer } ' +
25655         '</style>';
25656
25657         var cls = 'roo-htmleditor-body';
25658         
25659         if(this.bodyCls.length){
25660             cls += ' ' + this.bodyCls;
25661         }
25662         
25663         return '<html><head>' + st  +
25664             //<style type="text/css">' +
25665             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25666             //'</style>' +
25667             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25668     },
25669
25670     // private
25671     onRender : function(ct, position)
25672     {
25673         var _t = this;
25674         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25675         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25676         
25677         
25678         this.el.dom.style.border = '0 none';
25679         this.el.dom.setAttribute('tabIndex', -1);
25680         this.el.addClass('x-hidden hide');
25681         
25682         
25683         
25684         if(Roo.isIE){ // fix IE 1px bogus margin
25685             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25686         }
25687        
25688         
25689         this.frameId = Roo.id();
25690         
25691          
25692         
25693         var iframe = this.owner.wrap.createChild({
25694             tag: 'iframe',
25695             cls: 'form-control', // bootstrap..
25696             id: this.frameId,
25697             name: this.frameId,
25698             frameBorder : 'no',
25699             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25700         }, this.el
25701         );
25702         
25703         
25704         this.iframe = iframe.dom;
25705
25706          this.assignDocWin();
25707         
25708         this.doc.designMode = 'on';
25709        
25710         this.doc.open();
25711         this.doc.write(this.getDocMarkup());
25712         this.doc.close();
25713
25714         
25715         var task = { // must defer to wait for browser to be ready
25716             run : function(){
25717                 //console.log("run task?" + this.doc.readyState);
25718                 this.assignDocWin();
25719                 if(this.doc.body || this.doc.readyState == 'complete'){
25720                     try {
25721                         this.doc.designMode="on";
25722                     } catch (e) {
25723                         return;
25724                     }
25725                     Roo.TaskMgr.stop(task);
25726                     this.initEditor.defer(10, this);
25727                 }
25728             },
25729             interval : 10,
25730             duration: 10000,
25731             scope: this
25732         };
25733         Roo.TaskMgr.start(task);
25734
25735     },
25736
25737     // private
25738     onResize : function(w, h)
25739     {
25740          Roo.log('resize: ' +w + ',' + h );
25741         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25742         if(!this.iframe){
25743             return;
25744         }
25745         if(typeof w == 'number'){
25746             
25747             this.iframe.style.width = w + 'px';
25748         }
25749         if(typeof h == 'number'){
25750             
25751             this.iframe.style.height = h + 'px';
25752             if(this.doc){
25753                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25754             }
25755         }
25756         
25757     },
25758
25759     /**
25760      * Toggles the editor between standard and source edit mode.
25761      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25762      */
25763     toggleSourceEdit : function(sourceEditMode){
25764         
25765         this.sourceEditMode = sourceEditMode === true;
25766         
25767         if(this.sourceEditMode){
25768  
25769             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25770             
25771         }else{
25772             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25773             //this.iframe.className = '';
25774             this.deferFocus();
25775         }
25776         //this.setSize(this.owner.wrap.getSize());
25777         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25778     },
25779
25780     
25781   
25782
25783     /**
25784      * Protected method that will not generally be called directly. If you need/want
25785      * custom HTML cleanup, this is the method you should override.
25786      * @param {String} html The HTML to be cleaned
25787      * return {String} The cleaned HTML
25788      */
25789     cleanHtml : function(html){
25790         html = String(html);
25791         if(html.length > 5){
25792             if(Roo.isSafari){ // strip safari nonsense
25793                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25794             }
25795         }
25796         if(html == '&nbsp;'){
25797             html = '';
25798         }
25799         return html;
25800     },
25801
25802     /**
25803      * HTML Editor -> Textarea
25804      * Protected method that will not generally be called directly. Syncs the contents
25805      * of the editor iframe with the textarea.
25806      */
25807     syncValue : function(){
25808         if(this.initialized){
25809             var bd = (this.doc.body || this.doc.documentElement);
25810             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25811             var html = bd.innerHTML;
25812             if(Roo.isSafari){
25813                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25814                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25815                 if(m && m[1]){
25816                     html = '<div style="'+m[0]+'">' + html + '</div>';
25817                 }
25818             }
25819             html = this.cleanHtml(html);
25820             // fix up the special chars.. normaly like back quotes in word...
25821             // however we do not want to do this with chinese..
25822             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25823                 
25824                 var cc = match.charCodeAt();
25825
25826                 // Get the character value, handling surrogate pairs
25827                 if (match.length == 2) {
25828                     // It's a surrogate pair, calculate the Unicode code point
25829                     var high = match.charCodeAt(0) - 0xD800;
25830                     var low  = match.charCodeAt(1) - 0xDC00;
25831                     cc = (high * 0x400) + low + 0x10000;
25832                 }  else if (
25833                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25834                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25835                     (cc >= 0xf900 && cc < 0xfb00 )
25836                 ) {
25837                         return match;
25838                 }  
25839          
25840                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25841                 return "&#" + cc + ";";
25842                 
25843                 
25844             });
25845             
25846             
25847              
25848             if(this.owner.fireEvent('beforesync', this, html) !== false){
25849                 this.el.dom.value = html;
25850                 this.owner.fireEvent('sync', this, html);
25851             }
25852         }
25853     },
25854
25855     /**
25856      * Protected method that will not generally be called directly. Pushes the value of the textarea
25857      * into the iframe editor.
25858      */
25859     pushValue : function(){
25860         if(this.initialized){
25861             var v = this.el.dom.value.trim();
25862             
25863 //            if(v.length < 1){
25864 //                v = '&#160;';
25865 //            }
25866             
25867             if(this.owner.fireEvent('beforepush', this, v) !== false){
25868                 var d = (this.doc.body || this.doc.documentElement);
25869                 d.innerHTML = v;
25870                 this.cleanUpPaste();
25871                 this.el.dom.value = d.innerHTML;
25872                 this.owner.fireEvent('push', this, v);
25873             }
25874         }
25875     },
25876
25877     // private
25878     deferFocus : function(){
25879         this.focus.defer(10, this);
25880     },
25881
25882     // doc'ed in Field
25883     focus : function(){
25884         if(this.win && !this.sourceEditMode){
25885             this.win.focus();
25886         }else{
25887             this.el.focus();
25888         }
25889     },
25890     
25891     assignDocWin: function()
25892     {
25893         var iframe = this.iframe;
25894         
25895          if(Roo.isIE){
25896             this.doc = iframe.contentWindow.document;
25897             this.win = iframe.contentWindow;
25898         } else {
25899 //            if (!Roo.get(this.frameId)) {
25900 //                return;
25901 //            }
25902 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25903 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25904             
25905             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25906                 return;
25907             }
25908             
25909             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25910             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25911         }
25912     },
25913     
25914     // private
25915     initEditor : function(){
25916         //console.log("INIT EDITOR");
25917         this.assignDocWin();
25918         
25919         
25920         
25921         this.doc.designMode="on";
25922         this.doc.open();
25923         this.doc.write(this.getDocMarkup());
25924         this.doc.close();
25925         
25926         var dbody = (this.doc.body || this.doc.documentElement);
25927         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25928         // this copies styles from the containing element into thsi one..
25929         // not sure why we need all of this..
25930         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25931         
25932         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25933         //ss['background-attachment'] = 'fixed'; // w3c
25934         dbody.bgProperties = 'fixed'; // ie
25935         //Roo.DomHelper.applyStyles(dbody, ss);
25936         Roo.EventManager.on(this.doc, {
25937             //'mousedown': this.onEditorEvent,
25938             'mouseup': this.onEditorEvent,
25939             'dblclick': this.onEditorEvent,
25940             'click': this.onEditorEvent,
25941             'keyup': this.onEditorEvent,
25942             buffer:100,
25943             scope: this
25944         });
25945         if(Roo.isGecko){
25946             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25947         }
25948         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25949             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25950         }
25951         this.initialized = true;
25952
25953         this.owner.fireEvent('initialize', this);
25954         this.pushValue();
25955     },
25956
25957     // private
25958     onDestroy : function(){
25959         
25960         
25961         
25962         if(this.rendered){
25963             
25964             //for (var i =0; i < this.toolbars.length;i++) {
25965             //    // fixme - ask toolbars for heights?
25966             //    this.toolbars[i].onDestroy();
25967            // }
25968             
25969             //this.wrap.dom.innerHTML = '';
25970             //this.wrap.remove();
25971         }
25972     },
25973
25974     // private
25975     onFirstFocus : function(){
25976         
25977         this.assignDocWin();
25978         
25979         
25980         this.activated = true;
25981          
25982     
25983         if(Roo.isGecko){ // prevent silly gecko errors
25984             this.win.focus();
25985             var s = this.win.getSelection();
25986             if(!s.focusNode || s.focusNode.nodeType != 3){
25987                 var r = s.getRangeAt(0);
25988                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25989                 r.collapse(true);
25990                 this.deferFocus();
25991             }
25992             try{
25993                 this.execCmd('useCSS', true);
25994                 this.execCmd('styleWithCSS', false);
25995             }catch(e){}
25996         }
25997         this.owner.fireEvent('activate', this);
25998     },
25999
26000     // private
26001     adjustFont: function(btn){
26002         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26003         //if(Roo.isSafari){ // safari
26004         //    adjust *= 2;
26005        // }
26006         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26007         if(Roo.isSafari){ // safari
26008             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26009             v =  (v < 10) ? 10 : v;
26010             v =  (v > 48) ? 48 : v;
26011             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26012             
26013         }
26014         
26015         
26016         v = Math.max(1, v+adjust);
26017         
26018         this.execCmd('FontSize', v  );
26019     },
26020
26021     onEditorEvent : function(e)
26022     {
26023         this.owner.fireEvent('editorevent', this, e);
26024       //  this.updateToolbar();
26025         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26026     },
26027
26028     insertTag : function(tg)
26029     {
26030         // could be a bit smarter... -> wrap the current selected tRoo..
26031         if (tg.toLowerCase() == 'span' ||
26032             tg.toLowerCase() == 'code' ||
26033             tg.toLowerCase() == 'sup' ||
26034             tg.toLowerCase() == 'sub' 
26035             ) {
26036             
26037             range = this.createRange(this.getSelection());
26038             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26039             wrappingNode.appendChild(range.extractContents());
26040             range.insertNode(wrappingNode);
26041
26042             return;
26043             
26044             
26045             
26046         }
26047         this.execCmd("formatblock",   tg);
26048         
26049     },
26050     
26051     insertText : function(txt)
26052     {
26053         
26054         
26055         var range = this.createRange();
26056         range.deleteContents();
26057                //alert(Sender.getAttribute('label'));
26058                
26059         range.insertNode(this.doc.createTextNode(txt));
26060     } ,
26061     
26062      
26063
26064     /**
26065      * Executes a Midas editor command on the editor document and performs necessary focus and
26066      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26067      * @param {String} cmd The Midas command
26068      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26069      */
26070     relayCmd : function(cmd, value){
26071         this.win.focus();
26072         this.execCmd(cmd, value);
26073         this.owner.fireEvent('editorevent', this);
26074         //this.updateToolbar();
26075         this.owner.deferFocus();
26076     },
26077
26078     /**
26079      * Executes a Midas editor command directly on the editor document.
26080      * For visual commands, you should use {@link #relayCmd} instead.
26081      * <b>This should only be called after the editor is initialized.</b>
26082      * @param {String} cmd The Midas command
26083      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26084      */
26085     execCmd : function(cmd, value){
26086         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26087         this.syncValue();
26088     },
26089  
26090  
26091    
26092     /**
26093      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26094      * to insert tRoo.
26095      * @param {String} text | dom node.. 
26096      */
26097     insertAtCursor : function(text)
26098     {
26099         
26100         if(!this.activated){
26101             return;
26102         }
26103         /*
26104         if(Roo.isIE){
26105             this.win.focus();
26106             var r = this.doc.selection.createRange();
26107             if(r){
26108                 r.collapse(true);
26109                 r.pasteHTML(text);
26110                 this.syncValue();
26111                 this.deferFocus();
26112             
26113             }
26114             return;
26115         }
26116         */
26117         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26118             this.win.focus();
26119             
26120             
26121             // from jquery ui (MIT licenced)
26122             var range, node;
26123             var win = this.win;
26124             
26125             if (win.getSelection && win.getSelection().getRangeAt) {
26126                 range = win.getSelection().getRangeAt(0);
26127                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26128                 range.insertNode(node);
26129             } else if (win.document.selection && win.document.selection.createRange) {
26130                 // no firefox support
26131                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26132                 win.document.selection.createRange().pasteHTML(txt);
26133             } else {
26134                 // no firefox support
26135                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26136                 this.execCmd('InsertHTML', txt);
26137             } 
26138             
26139             this.syncValue();
26140             
26141             this.deferFocus();
26142         }
26143     },
26144  // private
26145     mozKeyPress : function(e){
26146         if(e.ctrlKey){
26147             var c = e.getCharCode(), cmd;
26148           
26149             if(c > 0){
26150                 c = String.fromCharCode(c).toLowerCase();
26151                 switch(c){
26152                     case 'b':
26153                         cmd = 'bold';
26154                         break;
26155                     case 'i':
26156                         cmd = 'italic';
26157                         break;
26158                     
26159                     case 'u':
26160                         cmd = 'underline';
26161                         break;
26162                     
26163                     case 'v':
26164                         this.cleanUpPaste.defer(100, this);
26165                         return;
26166                         
26167                 }
26168                 if(cmd){
26169                     this.win.focus();
26170                     this.execCmd(cmd);
26171                     this.deferFocus();
26172                     e.preventDefault();
26173                 }
26174                 
26175             }
26176         }
26177     },
26178
26179     // private
26180     fixKeys : function(){ // load time branching for fastest keydown performance
26181         if(Roo.isIE){
26182             return function(e){
26183                 var k = e.getKey(), r;
26184                 if(k == e.TAB){
26185                     e.stopEvent();
26186                     r = this.doc.selection.createRange();
26187                     if(r){
26188                         r.collapse(true);
26189                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26190                         this.deferFocus();
26191                     }
26192                     return;
26193                 }
26194                 
26195                 if(k == e.ENTER){
26196                     r = this.doc.selection.createRange();
26197                     if(r){
26198                         var target = r.parentElement();
26199                         if(!target || target.tagName.toLowerCase() != 'li'){
26200                             e.stopEvent();
26201                             r.pasteHTML('<br />');
26202                             r.collapse(false);
26203                             r.select();
26204                         }
26205                     }
26206                 }
26207                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26208                     this.cleanUpPaste.defer(100, this);
26209                     return;
26210                 }
26211                 
26212                 
26213             };
26214         }else if(Roo.isOpera){
26215             return function(e){
26216                 var k = e.getKey();
26217                 if(k == e.TAB){
26218                     e.stopEvent();
26219                     this.win.focus();
26220                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26221                     this.deferFocus();
26222                 }
26223                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26224                     this.cleanUpPaste.defer(100, this);
26225                     return;
26226                 }
26227                 
26228             };
26229         }else if(Roo.isSafari){
26230             return function(e){
26231                 var k = e.getKey();
26232                 
26233                 if(k == e.TAB){
26234                     e.stopEvent();
26235                     this.execCmd('InsertText','\t');
26236                     this.deferFocus();
26237                     return;
26238                 }
26239                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26240                     this.cleanUpPaste.defer(100, this);
26241                     return;
26242                 }
26243                 
26244              };
26245         }
26246     }(),
26247     
26248     getAllAncestors: function()
26249     {
26250         var p = this.getSelectedNode();
26251         var a = [];
26252         if (!p) {
26253             a.push(p); // push blank onto stack..
26254             p = this.getParentElement();
26255         }
26256         
26257         
26258         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26259             a.push(p);
26260             p = p.parentNode;
26261         }
26262         a.push(this.doc.body);
26263         return a;
26264     },
26265     lastSel : false,
26266     lastSelNode : false,
26267     
26268     
26269     getSelection : function() 
26270     {
26271         this.assignDocWin();
26272         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26273     },
26274     
26275     getSelectedNode: function() 
26276     {
26277         // this may only work on Gecko!!!
26278         
26279         // should we cache this!!!!
26280         
26281         
26282         
26283          
26284         var range = this.createRange(this.getSelection()).cloneRange();
26285         
26286         if (Roo.isIE) {
26287             var parent = range.parentElement();
26288             while (true) {
26289                 var testRange = range.duplicate();
26290                 testRange.moveToElementText(parent);
26291                 if (testRange.inRange(range)) {
26292                     break;
26293                 }
26294                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26295                     break;
26296                 }
26297                 parent = parent.parentElement;
26298             }
26299             return parent;
26300         }
26301         
26302         // is ancestor a text element.
26303         var ac =  range.commonAncestorContainer;
26304         if (ac.nodeType == 3) {
26305             ac = ac.parentNode;
26306         }
26307         
26308         var ar = ac.childNodes;
26309          
26310         var nodes = [];
26311         var other_nodes = [];
26312         var has_other_nodes = false;
26313         for (var i=0;i<ar.length;i++) {
26314             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26315                 continue;
26316             }
26317             // fullly contained node.
26318             
26319             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26320                 nodes.push(ar[i]);
26321                 continue;
26322             }
26323             
26324             // probably selected..
26325             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26326                 other_nodes.push(ar[i]);
26327                 continue;
26328             }
26329             // outer..
26330             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26331                 continue;
26332             }
26333             
26334             
26335             has_other_nodes = true;
26336         }
26337         if (!nodes.length && other_nodes.length) {
26338             nodes= other_nodes;
26339         }
26340         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26341             return false;
26342         }
26343         
26344         return nodes[0];
26345     },
26346     createRange: function(sel)
26347     {
26348         // this has strange effects when using with 
26349         // top toolbar - not sure if it's a great idea.
26350         //this.editor.contentWindow.focus();
26351         if (typeof sel != "undefined") {
26352             try {
26353                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26354             } catch(e) {
26355                 return this.doc.createRange();
26356             }
26357         } else {
26358             return this.doc.createRange();
26359         }
26360     },
26361     getParentElement: function()
26362     {
26363         
26364         this.assignDocWin();
26365         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26366         
26367         var range = this.createRange(sel);
26368          
26369         try {
26370             var p = range.commonAncestorContainer;
26371             while (p.nodeType == 3) { // text node
26372                 p = p.parentNode;
26373             }
26374             return p;
26375         } catch (e) {
26376             return null;
26377         }
26378     
26379     },
26380     /***
26381      *
26382      * Range intersection.. the hard stuff...
26383      *  '-1' = before
26384      *  '0' = hits..
26385      *  '1' = after.
26386      *         [ -- selected range --- ]
26387      *   [fail]                        [fail]
26388      *
26389      *    basically..
26390      *      if end is before start or  hits it. fail.
26391      *      if start is after end or hits it fail.
26392      *
26393      *   if either hits (but other is outside. - then it's not 
26394      *   
26395      *    
26396      **/
26397     
26398     
26399     // @see http://www.thismuchiknow.co.uk/?p=64.
26400     rangeIntersectsNode : function(range, node)
26401     {
26402         var nodeRange = node.ownerDocument.createRange();
26403         try {
26404             nodeRange.selectNode(node);
26405         } catch (e) {
26406             nodeRange.selectNodeContents(node);
26407         }
26408     
26409         var rangeStartRange = range.cloneRange();
26410         rangeStartRange.collapse(true);
26411     
26412         var rangeEndRange = range.cloneRange();
26413         rangeEndRange.collapse(false);
26414     
26415         var nodeStartRange = nodeRange.cloneRange();
26416         nodeStartRange.collapse(true);
26417     
26418         var nodeEndRange = nodeRange.cloneRange();
26419         nodeEndRange.collapse(false);
26420     
26421         return rangeStartRange.compareBoundaryPoints(
26422                  Range.START_TO_START, nodeEndRange) == -1 &&
26423                rangeEndRange.compareBoundaryPoints(
26424                  Range.START_TO_START, nodeStartRange) == 1;
26425         
26426          
26427     },
26428     rangeCompareNode : function(range, node)
26429     {
26430         var nodeRange = node.ownerDocument.createRange();
26431         try {
26432             nodeRange.selectNode(node);
26433         } catch (e) {
26434             nodeRange.selectNodeContents(node);
26435         }
26436         
26437         
26438         range.collapse(true);
26439     
26440         nodeRange.collapse(true);
26441      
26442         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26443         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26444          
26445         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26446         
26447         var nodeIsBefore   =  ss == 1;
26448         var nodeIsAfter    = ee == -1;
26449         
26450         if (nodeIsBefore && nodeIsAfter) {
26451             return 0; // outer
26452         }
26453         if (!nodeIsBefore && nodeIsAfter) {
26454             return 1; //right trailed.
26455         }
26456         
26457         if (nodeIsBefore && !nodeIsAfter) {
26458             return 2;  // left trailed.
26459         }
26460         // fully contined.
26461         return 3;
26462     },
26463
26464     // private? - in a new class?
26465     cleanUpPaste :  function()
26466     {
26467         // cleans up the whole document..
26468         Roo.log('cleanuppaste');
26469         
26470         this.cleanUpChildren(this.doc.body);
26471         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26472         if (clean != this.doc.body.innerHTML) {
26473             this.doc.body.innerHTML = clean;
26474         }
26475         
26476     },
26477     
26478     cleanWordChars : function(input) {// change the chars to hex code
26479         var he = Roo.HtmlEditorCore;
26480         
26481         var output = input;
26482         Roo.each(he.swapCodes, function(sw) { 
26483             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26484             
26485             output = output.replace(swapper, sw[1]);
26486         });
26487         
26488         return output;
26489     },
26490     
26491     
26492     cleanUpChildren : function (n)
26493     {
26494         if (!n.childNodes.length) {
26495             return;
26496         }
26497         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26498            this.cleanUpChild(n.childNodes[i]);
26499         }
26500     },
26501     
26502     
26503         
26504     
26505     cleanUpChild : function (node)
26506     {
26507         var ed = this;
26508         //console.log(node);
26509         if (node.nodeName == "#text") {
26510             // clean up silly Windows -- stuff?
26511             return; 
26512         }
26513         if (node.nodeName == "#comment") {
26514             node.parentNode.removeChild(node);
26515             // clean up silly Windows -- stuff?
26516             return; 
26517         }
26518         var lcname = node.tagName.toLowerCase();
26519         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26520         // whitelist of tags..
26521         
26522         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26523             // remove node.
26524             node.parentNode.removeChild(node);
26525             return;
26526             
26527         }
26528         
26529         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26530         
26531         // spans with no attributes - just remove them..
26532         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26533             remove_keep_children = true;
26534         }
26535         
26536         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26537         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26538         
26539         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26540         //    remove_keep_children = true;
26541         //}
26542         
26543         if (remove_keep_children) {
26544             this.cleanUpChildren(node);
26545             // inserts everything just before this node...
26546             while (node.childNodes.length) {
26547                 var cn = node.childNodes[0];
26548                 node.removeChild(cn);
26549                 node.parentNode.insertBefore(cn, node);
26550             }
26551             node.parentNode.removeChild(node);
26552             return;
26553         }
26554         
26555         if (!node.attributes || !node.attributes.length) {
26556             
26557           
26558             
26559             
26560             this.cleanUpChildren(node);
26561             return;
26562         }
26563         
26564         function cleanAttr(n,v)
26565         {
26566             
26567             if (v.match(/^\./) || v.match(/^\//)) {
26568                 return;
26569             }
26570             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26571                 return;
26572             }
26573             if (v.match(/^#/)) {
26574                 return;
26575             }
26576             if (v.match(/^\{/)) { // allow template editing.
26577                 return;
26578             }
26579 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26580             node.removeAttribute(n);
26581             
26582         }
26583         
26584         var cwhite = this.cwhite;
26585         var cblack = this.cblack;
26586             
26587         function cleanStyle(n,v)
26588         {
26589             if (v.match(/expression/)) { //XSS?? should we even bother..
26590                 node.removeAttribute(n);
26591                 return;
26592             }
26593             
26594             var parts = v.split(/;/);
26595             var clean = [];
26596             
26597             Roo.each(parts, function(p) {
26598                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26599                 if (!p.length) {
26600                     return true;
26601                 }
26602                 var l = p.split(':').shift().replace(/\s+/g,'');
26603                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26604                 
26605                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26606 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26607                     //node.removeAttribute(n);
26608                     return true;
26609                 }
26610                 //Roo.log()
26611                 // only allow 'c whitelisted system attributes'
26612                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26613 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26614                     //node.removeAttribute(n);
26615                     return true;
26616                 }
26617                 
26618                 
26619                  
26620                 
26621                 clean.push(p);
26622                 return true;
26623             });
26624             if (clean.length) { 
26625                 node.setAttribute(n, clean.join(';'));
26626             } else {
26627                 node.removeAttribute(n);
26628             }
26629             
26630         }
26631         
26632         
26633         for (var i = node.attributes.length-1; i > -1 ; i--) {
26634             var a = node.attributes[i];
26635             //console.log(a);
26636             
26637             if (a.name.toLowerCase().substr(0,2)=='on')  {
26638                 node.removeAttribute(a.name);
26639                 continue;
26640             }
26641             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26642                 node.removeAttribute(a.name);
26643                 continue;
26644             }
26645             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26646                 cleanAttr(a.name,a.value); // fixme..
26647                 continue;
26648             }
26649             if (a.name == 'style') {
26650                 cleanStyle(a.name,a.value);
26651                 continue;
26652             }
26653             /// clean up MS crap..
26654             // tecnically this should be a list of valid class'es..
26655             
26656             
26657             if (a.name == 'class') {
26658                 if (a.value.match(/^Mso/)) {
26659                     node.removeAttribute('class');
26660                 }
26661                 
26662                 if (a.value.match(/^body$/)) {
26663                     node.removeAttribute('class');
26664                 }
26665                 continue;
26666             }
26667             
26668             // style cleanup!?
26669             // class cleanup?
26670             
26671         }
26672         
26673         
26674         this.cleanUpChildren(node);
26675         
26676         
26677     },
26678     
26679     /**
26680      * Clean up MS wordisms...
26681      */
26682     cleanWord : function(node)
26683     {
26684         if (!node) {
26685             this.cleanWord(this.doc.body);
26686             return;
26687         }
26688         
26689         if(
26690                 node.nodeName == 'SPAN' &&
26691                 !node.hasAttributes() &&
26692                 node.childNodes.length == 1 &&
26693                 node.firstChild.nodeName == "#text"  
26694         ) {
26695             var textNode = node.firstChild;
26696             node.removeChild(textNode);
26697             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26698                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26699             }
26700             node.parentNode.insertBefore(textNode, node);
26701             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26702                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26703             }
26704             node.parentNode.removeChild(node);
26705         }
26706         
26707         if (node.nodeName == "#text") {
26708             // clean up silly Windows -- stuff?
26709             return; 
26710         }
26711         if (node.nodeName == "#comment") {
26712             node.parentNode.removeChild(node);
26713             // clean up silly Windows -- stuff?
26714             return; 
26715         }
26716         
26717         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26718             node.parentNode.removeChild(node);
26719             return;
26720         }
26721         //Roo.log(node.tagName);
26722         // remove - but keep children..
26723         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26724             //Roo.log('-- removed');
26725             while (node.childNodes.length) {
26726                 var cn = node.childNodes[0];
26727                 node.removeChild(cn);
26728                 node.parentNode.insertBefore(cn, node);
26729                 // move node to parent - and clean it..
26730                 this.cleanWord(cn);
26731             }
26732             node.parentNode.removeChild(node);
26733             /// no need to iterate chidlren = it's got none..
26734             //this.iterateChildren(node, this.cleanWord);
26735             return;
26736         }
26737         // clean styles
26738         if (node.className.length) {
26739             
26740             var cn = node.className.split(/\W+/);
26741             var cna = [];
26742             Roo.each(cn, function(cls) {
26743                 if (cls.match(/Mso[a-zA-Z]+/)) {
26744                     return;
26745                 }
26746                 cna.push(cls);
26747             });
26748             node.className = cna.length ? cna.join(' ') : '';
26749             if (!cna.length) {
26750                 node.removeAttribute("class");
26751             }
26752         }
26753         
26754         if (node.hasAttribute("lang")) {
26755             node.removeAttribute("lang");
26756         }
26757         
26758         if (node.hasAttribute("style")) {
26759             
26760             var styles = node.getAttribute("style").split(";");
26761             var nstyle = [];
26762             Roo.each(styles, function(s) {
26763                 if (!s.match(/:/)) {
26764                     return;
26765                 }
26766                 var kv = s.split(":");
26767                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26768                     return;
26769                 }
26770                 // what ever is left... we allow.
26771                 nstyle.push(s);
26772             });
26773             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26774             if (!nstyle.length) {
26775                 node.removeAttribute('style');
26776             }
26777         }
26778         this.iterateChildren(node, this.cleanWord);
26779         
26780         
26781         
26782     },
26783     /**
26784      * iterateChildren of a Node, calling fn each time, using this as the scole..
26785      * @param {DomNode} node node to iterate children of.
26786      * @param {Function} fn method of this class to call on each item.
26787      */
26788     iterateChildren : function(node, fn)
26789     {
26790         if (!node.childNodes.length) {
26791                 return;
26792         }
26793         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26794            fn.call(this, node.childNodes[i])
26795         }
26796     },
26797     
26798     
26799     /**
26800      * cleanTableWidths.
26801      *
26802      * Quite often pasting from word etc.. results in tables with column and widths.
26803      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26804      *
26805      */
26806     cleanTableWidths : function(node)
26807     {
26808          
26809          
26810         if (!node) {
26811             this.cleanTableWidths(this.doc.body);
26812             return;
26813         }
26814         
26815         // ignore list...
26816         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26817             return; 
26818         }
26819         Roo.log(node.tagName);
26820         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26821             this.iterateChildren(node, this.cleanTableWidths);
26822             return;
26823         }
26824         if (node.hasAttribute('width')) {
26825             node.removeAttribute('width');
26826         }
26827         
26828          
26829         if (node.hasAttribute("style")) {
26830             // pretty basic...
26831             
26832             var styles = node.getAttribute("style").split(";");
26833             var nstyle = [];
26834             Roo.each(styles, function(s) {
26835                 if (!s.match(/:/)) {
26836                     return;
26837                 }
26838                 var kv = s.split(":");
26839                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26840                     return;
26841                 }
26842                 // what ever is left... we allow.
26843                 nstyle.push(s);
26844             });
26845             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26846             if (!nstyle.length) {
26847                 node.removeAttribute('style');
26848             }
26849         }
26850         
26851         this.iterateChildren(node, this.cleanTableWidths);
26852         
26853         
26854     },
26855     
26856     
26857     
26858     
26859     domToHTML : function(currentElement, depth, nopadtext) {
26860         
26861         depth = depth || 0;
26862         nopadtext = nopadtext || false;
26863     
26864         if (!currentElement) {
26865             return this.domToHTML(this.doc.body);
26866         }
26867         
26868         //Roo.log(currentElement);
26869         var j;
26870         var allText = false;
26871         var nodeName = currentElement.nodeName;
26872         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26873         
26874         if  (nodeName == '#text') {
26875             
26876             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26877         }
26878         
26879         
26880         var ret = '';
26881         if (nodeName != 'BODY') {
26882              
26883             var i = 0;
26884             // Prints the node tagName, such as <A>, <IMG>, etc
26885             if (tagName) {
26886                 var attr = [];
26887                 for(i = 0; i < currentElement.attributes.length;i++) {
26888                     // quoting?
26889                     var aname = currentElement.attributes.item(i).name;
26890                     if (!currentElement.attributes.item(i).value.length) {
26891                         continue;
26892                     }
26893                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26894                 }
26895                 
26896                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26897             } 
26898             else {
26899                 
26900                 // eack
26901             }
26902         } else {
26903             tagName = false;
26904         }
26905         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26906             return ret;
26907         }
26908         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26909             nopadtext = true;
26910         }
26911         
26912         
26913         // Traverse the tree
26914         i = 0;
26915         var currentElementChild = currentElement.childNodes.item(i);
26916         var allText = true;
26917         var innerHTML  = '';
26918         lastnode = '';
26919         while (currentElementChild) {
26920             // Formatting code (indent the tree so it looks nice on the screen)
26921             var nopad = nopadtext;
26922             if (lastnode == 'SPAN') {
26923                 nopad  = true;
26924             }
26925             // text
26926             if  (currentElementChild.nodeName == '#text') {
26927                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26928                 toadd = nopadtext ? toadd : toadd.trim();
26929                 if (!nopad && toadd.length > 80) {
26930                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26931                 }
26932                 innerHTML  += toadd;
26933                 
26934                 i++;
26935                 currentElementChild = currentElement.childNodes.item(i);
26936                 lastNode = '';
26937                 continue;
26938             }
26939             allText = false;
26940             
26941             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26942                 
26943             // Recursively traverse the tree structure of the child node
26944             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26945             lastnode = currentElementChild.nodeName;
26946             i++;
26947             currentElementChild=currentElement.childNodes.item(i);
26948         }
26949         
26950         ret += innerHTML;
26951         
26952         if (!allText) {
26953                 // The remaining code is mostly for formatting the tree
26954             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26955         }
26956         
26957         
26958         if (tagName) {
26959             ret+= "</"+tagName+">";
26960         }
26961         return ret;
26962         
26963     },
26964         
26965     applyBlacklists : function()
26966     {
26967         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26968         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26969         
26970         this.white = [];
26971         this.black = [];
26972         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26973             if (b.indexOf(tag) > -1) {
26974                 return;
26975             }
26976             this.white.push(tag);
26977             
26978         }, this);
26979         
26980         Roo.each(w, function(tag) {
26981             if (b.indexOf(tag) > -1) {
26982                 return;
26983             }
26984             if (this.white.indexOf(tag) > -1) {
26985                 return;
26986             }
26987             this.white.push(tag);
26988             
26989         }, this);
26990         
26991         
26992         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26993             if (w.indexOf(tag) > -1) {
26994                 return;
26995             }
26996             this.black.push(tag);
26997             
26998         }, this);
26999         
27000         Roo.each(b, function(tag) {
27001             if (w.indexOf(tag) > -1) {
27002                 return;
27003             }
27004             if (this.black.indexOf(tag) > -1) {
27005                 return;
27006             }
27007             this.black.push(tag);
27008             
27009         }, this);
27010         
27011         
27012         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27013         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27014         
27015         this.cwhite = [];
27016         this.cblack = [];
27017         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27018             if (b.indexOf(tag) > -1) {
27019                 return;
27020             }
27021             this.cwhite.push(tag);
27022             
27023         }, this);
27024         
27025         Roo.each(w, function(tag) {
27026             if (b.indexOf(tag) > -1) {
27027                 return;
27028             }
27029             if (this.cwhite.indexOf(tag) > -1) {
27030                 return;
27031             }
27032             this.cwhite.push(tag);
27033             
27034         }, this);
27035         
27036         
27037         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27038             if (w.indexOf(tag) > -1) {
27039                 return;
27040             }
27041             this.cblack.push(tag);
27042             
27043         }, this);
27044         
27045         Roo.each(b, function(tag) {
27046             if (w.indexOf(tag) > -1) {
27047                 return;
27048             }
27049             if (this.cblack.indexOf(tag) > -1) {
27050                 return;
27051             }
27052             this.cblack.push(tag);
27053             
27054         }, this);
27055     },
27056     
27057     setStylesheets : function(stylesheets)
27058     {
27059         if(typeof(stylesheets) == 'string'){
27060             Roo.get(this.iframe.contentDocument.head).createChild({
27061                 tag : 'link',
27062                 rel : 'stylesheet',
27063                 type : 'text/css',
27064                 href : stylesheets
27065             });
27066             
27067             return;
27068         }
27069         var _this = this;
27070      
27071         Roo.each(stylesheets, function(s) {
27072             if(!s.length){
27073                 return;
27074             }
27075             
27076             Roo.get(_this.iframe.contentDocument.head).createChild({
27077                 tag : 'link',
27078                 rel : 'stylesheet',
27079                 type : 'text/css',
27080                 href : s
27081             });
27082         });
27083
27084         
27085     },
27086     
27087     removeStylesheets : function()
27088     {
27089         var _this = this;
27090         
27091         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27092             s.remove();
27093         });
27094     },
27095     
27096     setStyle : function(style)
27097     {
27098         Roo.get(this.iframe.contentDocument.head).createChild({
27099             tag : 'style',
27100             type : 'text/css',
27101             html : style
27102         });
27103
27104         return;
27105     }
27106     
27107     // hide stuff that is not compatible
27108     /**
27109      * @event blur
27110      * @hide
27111      */
27112     /**
27113      * @event change
27114      * @hide
27115      */
27116     /**
27117      * @event focus
27118      * @hide
27119      */
27120     /**
27121      * @event specialkey
27122      * @hide
27123      */
27124     /**
27125      * @cfg {String} fieldClass @hide
27126      */
27127     /**
27128      * @cfg {String} focusClass @hide
27129      */
27130     /**
27131      * @cfg {String} autoCreate @hide
27132      */
27133     /**
27134      * @cfg {String} inputType @hide
27135      */
27136     /**
27137      * @cfg {String} invalidClass @hide
27138      */
27139     /**
27140      * @cfg {String} invalidText @hide
27141      */
27142     /**
27143      * @cfg {String} msgFx @hide
27144      */
27145     /**
27146      * @cfg {String} validateOnBlur @hide
27147      */
27148 });
27149
27150 Roo.HtmlEditorCore.white = [
27151         'area', 'br', 'img', 'input', 'hr', 'wbr',
27152         
27153        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27154        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27155        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27156        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27157        'table',   'ul',         'xmp', 
27158        
27159        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27160       'thead',   'tr', 
27161      
27162       'dir', 'menu', 'ol', 'ul', 'dl',
27163        
27164       'embed',  'object'
27165 ];
27166
27167
27168 Roo.HtmlEditorCore.black = [
27169     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27170         'applet', // 
27171         'base',   'basefont', 'bgsound', 'blink',  'body', 
27172         'frame',  'frameset', 'head',    'html',   'ilayer', 
27173         'iframe', 'layer',  'link',     'meta',    'object',   
27174         'script', 'style' ,'title',  'xml' // clean later..
27175 ];
27176 Roo.HtmlEditorCore.clean = [
27177     'script', 'style', 'title', 'xml'
27178 ];
27179 Roo.HtmlEditorCore.remove = [
27180     'font'
27181 ];
27182 // attributes..
27183
27184 Roo.HtmlEditorCore.ablack = [
27185     'on'
27186 ];
27187     
27188 Roo.HtmlEditorCore.aclean = [ 
27189     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27190 ];
27191
27192 // protocols..
27193 Roo.HtmlEditorCore.pwhite= [
27194         'http',  'https',  'mailto'
27195 ];
27196
27197 // white listed style attributes.
27198 Roo.HtmlEditorCore.cwhite= [
27199       //  'text-align', /// default is to allow most things..
27200       
27201          
27202 //        'font-size'//??
27203 ];
27204
27205 // black listed style attributes.
27206 Roo.HtmlEditorCore.cblack= [
27207       //  'font-size' -- this can be set by the project 
27208 ];
27209
27210
27211 Roo.HtmlEditorCore.swapCodes   =[ 
27212     [    8211, "&#8211;" ], 
27213     [    8212, "&#8212;" ], 
27214     [    8216,  "'" ],  
27215     [    8217, "'" ],  
27216     [    8220, '"' ],  
27217     [    8221, '"' ],  
27218     [    8226, "*" ],  
27219     [    8230, "..." ]
27220 ]; 
27221
27222     /*
27223  * - LGPL
27224  *
27225  * HtmlEditor
27226  * 
27227  */
27228
27229 /**
27230  * @class Roo.bootstrap.HtmlEditor
27231  * @extends Roo.bootstrap.TextArea
27232  * Bootstrap HtmlEditor class
27233
27234  * @constructor
27235  * Create a new HtmlEditor
27236  * @param {Object} config The config object
27237  */
27238
27239 Roo.bootstrap.HtmlEditor = function(config){
27240     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27241     if (!this.toolbars) {
27242         this.toolbars = [];
27243     }
27244     
27245     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27246     this.addEvents({
27247             /**
27248              * @event initialize
27249              * Fires when the editor is fully initialized (including the iframe)
27250              * @param {HtmlEditor} this
27251              */
27252             initialize: true,
27253             /**
27254              * @event activate
27255              * Fires when the editor is first receives the focus. Any insertion must wait
27256              * until after this event.
27257              * @param {HtmlEditor} this
27258              */
27259             activate: true,
27260              /**
27261              * @event beforesync
27262              * Fires before the textarea is updated with content from the editor iframe. Return false
27263              * to cancel the sync.
27264              * @param {HtmlEditor} this
27265              * @param {String} html
27266              */
27267             beforesync: true,
27268              /**
27269              * @event beforepush
27270              * Fires before the iframe editor is updated with content from the textarea. Return false
27271              * to cancel the push.
27272              * @param {HtmlEditor} this
27273              * @param {String} html
27274              */
27275             beforepush: true,
27276              /**
27277              * @event sync
27278              * Fires when the textarea is updated with content from the editor iframe.
27279              * @param {HtmlEditor} this
27280              * @param {String} html
27281              */
27282             sync: true,
27283              /**
27284              * @event push
27285              * Fires when the iframe editor is updated with content from the textarea.
27286              * @param {HtmlEditor} this
27287              * @param {String} html
27288              */
27289             push: true,
27290              /**
27291              * @event editmodechange
27292              * Fires when the editor switches edit modes
27293              * @param {HtmlEditor} this
27294              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27295              */
27296             editmodechange: true,
27297             /**
27298              * @event editorevent
27299              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27300              * @param {HtmlEditor} this
27301              */
27302             editorevent: true,
27303             /**
27304              * @event firstfocus
27305              * Fires when on first focus - needed by toolbars..
27306              * @param {HtmlEditor} this
27307              */
27308             firstfocus: true,
27309             /**
27310              * @event autosave
27311              * Auto save the htmlEditor value as a file into Events
27312              * @param {HtmlEditor} this
27313              */
27314             autosave: true,
27315             /**
27316              * @event savedpreview
27317              * preview the saved version of htmlEditor
27318              * @param {HtmlEditor} this
27319              */
27320             savedpreview: true
27321         });
27322 };
27323
27324
27325 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27326     
27327     
27328       /**
27329      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27330      */
27331     toolbars : false,
27332     
27333      /**
27334     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27335     */
27336     btns : [],
27337    
27338      /**
27339      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27340      *                        Roo.resizable.
27341      */
27342     resizable : false,
27343      /**
27344      * @cfg {Number} height (in pixels)
27345      */   
27346     height: 300,
27347    /**
27348      * @cfg {Number} width (in pixels)
27349      */   
27350     width: false,
27351     
27352     /**
27353      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27354      * 
27355      */
27356     stylesheets: false,
27357     
27358     // id of frame..
27359     frameId: false,
27360     
27361     // private properties
27362     validationEvent : false,
27363     deferHeight: true,
27364     initialized : false,
27365     activated : false,
27366     
27367     onFocus : Roo.emptyFn,
27368     iframePad:3,
27369     hideMode:'offsets',
27370     
27371     tbContainer : false,
27372     
27373     bodyCls : '',
27374     
27375     toolbarContainer :function() {
27376         return this.wrap.select('.x-html-editor-tb',true).first();
27377     },
27378
27379     /**
27380      * Protected method that will not generally be called directly. It
27381      * is called when the editor creates its toolbar. Override this method if you need to
27382      * add custom toolbar buttons.
27383      * @param {HtmlEditor} editor
27384      */
27385     createToolbar : function(){
27386         Roo.log('renewing');
27387         Roo.log("create toolbars");
27388         
27389         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27390         this.toolbars[0].render(this.toolbarContainer());
27391         
27392         return;
27393         
27394 //        if (!editor.toolbars || !editor.toolbars.length) {
27395 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27396 //        }
27397 //        
27398 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27399 //            editor.toolbars[i] = Roo.factory(
27400 //                    typeof(editor.toolbars[i]) == 'string' ?
27401 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27402 //                Roo.bootstrap.HtmlEditor);
27403 //            editor.toolbars[i].init(editor);
27404 //        }
27405     },
27406
27407      
27408     // private
27409     onRender : function(ct, position)
27410     {
27411        // Roo.log("Call onRender: " + this.xtype);
27412         var _t = this;
27413         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27414       
27415         this.wrap = this.inputEl().wrap({
27416             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27417         });
27418         
27419         this.editorcore.onRender(ct, position);
27420          
27421         if (this.resizable) {
27422             this.resizeEl = new Roo.Resizable(this.wrap, {
27423                 pinned : true,
27424                 wrap: true,
27425                 dynamic : true,
27426                 minHeight : this.height,
27427                 height: this.height,
27428                 handles : this.resizable,
27429                 width: this.width,
27430                 listeners : {
27431                     resize : function(r, w, h) {
27432                         _t.onResize(w,h); // -something
27433                     }
27434                 }
27435             });
27436             
27437         }
27438         this.createToolbar(this);
27439        
27440         
27441         if(!this.width && this.resizable){
27442             this.setSize(this.wrap.getSize());
27443         }
27444         if (this.resizeEl) {
27445             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27446             // should trigger onReize..
27447         }
27448         
27449     },
27450
27451     // private
27452     onResize : function(w, h)
27453     {
27454         Roo.log('resize: ' +w + ',' + h );
27455         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27456         var ew = false;
27457         var eh = false;
27458         
27459         if(this.inputEl() ){
27460             if(typeof w == 'number'){
27461                 var aw = w - this.wrap.getFrameWidth('lr');
27462                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27463                 ew = aw;
27464             }
27465             if(typeof h == 'number'){
27466                  var tbh = -11;  // fixme it needs to tool bar size!
27467                 for (var i =0; i < this.toolbars.length;i++) {
27468                     // fixme - ask toolbars for heights?
27469                     tbh += this.toolbars[i].el.getHeight();
27470                     //if (this.toolbars[i].footer) {
27471                     //    tbh += this.toolbars[i].footer.el.getHeight();
27472                     //}
27473                 }
27474               
27475                 
27476                 
27477                 
27478                 
27479                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27480                 ah -= 5; // knock a few pixes off for look..
27481                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27482                 var eh = ah;
27483             }
27484         }
27485         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27486         this.editorcore.onResize(ew,eh);
27487         
27488     },
27489
27490     /**
27491      * Toggles the editor between standard and source edit mode.
27492      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27493      */
27494     toggleSourceEdit : function(sourceEditMode)
27495     {
27496         this.editorcore.toggleSourceEdit(sourceEditMode);
27497         
27498         if(this.editorcore.sourceEditMode){
27499             Roo.log('editor - showing textarea');
27500             
27501 //            Roo.log('in');
27502 //            Roo.log(this.syncValue());
27503             this.syncValue();
27504             this.inputEl().removeClass(['hide', 'x-hidden']);
27505             this.inputEl().dom.removeAttribute('tabIndex');
27506             this.inputEl().focus();
27507         }else{
27508             Roo.log('editor - hiding textarea');
27509 //            Roo.log('out')
27510 //            Roo.log(this.pushValue()); 
27511             this.pushValue();
27512             
27513             this.inputEl().addClass(['hide', 'x-hidden']);
27514             this.inputEl().dom.setAttribute('tabIndex', -1);
27515             //this.deferFocus();
27516         }
27517          
27518         if(this.resizable){
27519             this.setSize(this.wrap.getSize());
27520         }
27521         
27522         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27523     },
27524  
27525     // private (for BoxComponent)
27526     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27527
27528     // private (for BoxComponent)
27529     getResizeEl : function(){
27530         return this.wrap;
27531     },
27532
27533     // private (for BoxComponent)
27534     getPositionEl : function(){
27535         return this.wrap;
27536     },
27537
27538     // private
27539     initEvents : function(){
27540         this.originalValue = this.getValue();
27541     },
27542
27543 //    /**
27544 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27545 //     * @method
27546 //     */
27547 //    markInvalid : Roo.emptyFn,
27548 //    /**
27549 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27550 //     * @method
27551 //     */
27552 //    clearInvalid : Roo.emptyFn,
27553
27554     setValue : function(v){
27555         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27556         this.editorcore.pushValue();
27557     },
27558
27559      
27560     // private
27561     deferFocus : function(){
27562         this.focus.defer(10, this);
27563     },
27564
27565     // doc'ed in Field
27566     focus : function(){
27567         this.editorcore.focus();
27568         
27569     },
27570       
27571
27572     // private
27573     onDestroy : function(){
27574         
27575         
27576         
27577         if(this.rendered){
27578             
27579             for (var i =0; i < this.toolbars.length;i++) {
27580                 // fixme - ask toolbars for heights?
27581                 this.toolbars[i].onDestroy();
27582             }
27583             
27584             this.wrap.dom.innerHTML = '';
27585             this.wrap.remove();
27586         }
27587     },
27588
27589     // private
27590     onFirstFocus : function(){
27591         //Roo.log("onFirstFocus");
27592         this.editorcore.onFirstFocus();
27593          for (var i =0; i < this.toolbars.length;i++) {
27594             this.toolbars[i].onFirstFocus();
27595         }
27596         
27597     },
27598     
27599     // private
27600     syncValue : function()
27601     {   
27602         this.editorcore.syncValue();
27603     },
27604     
27605     pushValue : function()
27606     {   
27607         this.editorcore.pushValue();
27608     }
27609      
27610     
27611     // hide stuff that is not compatible
27612     /**
27613      * @event blur
27614      * @hide
27615      */
27616     /**
27617      * @event change
27618      * @hide
27619      */
27620     /**
27621      * @event focus
27622      * @hide
27623      */
27624     /**
27625      * @event specialkey
27626      * @hide
27627      */
27628     /**
27629      * @cfg {String} fieldClass @hide
27630      */
27631     /**
27632      * @cfg {String} focusClass @hide
27633      */
27634     /**
27635      * @cfg {String} autoCreate @hide
27636      */
27637     /**
27638      * @cfg {String} inputType @hide
27639      */
27640      
27641     /**
27642      * @cfg {String} invalidText @hide
27643      */
27644     /**
27645      * @cfg {String} msgFx @hide
27646      */
27647     /**
27648      * @cfg {String} validateOnBlur @hide
27649      */
27650 });
27651  
27652     
27653    
27654    
27655    
27656       
27657 Roo.namespace('Roo.bootstrap.htmleditor');
27658 /**
27659  * @class Roo.bootstrap.HtmlEditorToolbar1
27660  * Basic Toolbar
27661  * 
27662  * @example
27663  * Usage:
27664  *
27665  new Roo.bootstrap.HtmlEditor({
27666     ....
27667     toolbars : [
27668         new Roo.bootstrap.HtmlEditorToolbar1({
27669             disable : { fonts: 1 , format: 1, ..., ... , ...],
27670             btns : [ .... ]
27671         })
27672     }
27673      
27674  * 
27675  * @cfg {Object} disable List of elements to disable..
27676  * @cfg {Array} btns List of additional buttons.
27677  * 
27678  * 
27679  * NEEDS Extra CSS? 
27680  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27681  */
27682  
27683 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27684 {
27685     
27686     Roo.apply(this, config);
27687     
27688     // default disabled, based on 'good practice'..
27689     this.disable = this.disable || {};
27690     Roo.applyIf(this.disable, {
27691         fontSize : true,
27692         colors : true,
27693         specialElements : true
27694     });
27695     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27696     
27697     this.editor = config.editor;
27698     this.editorcore = config.editor.editorcore;
27699     
27700     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27701     
27702     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27703     // dont call parent... till later.
27704 }
27705 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27706      
27707     bar : true,
27708     
27709     editor : false,
27710     editorcore : false,
27711     
27712     
27713     formats : [
27714         "p" ,  
27715         "h1","h2","h3","h4","h5","h6", 
27716         "pre", "code", 
27717         "abbr", "acronym", "address", "cite", "samp", "var",
27718         'div','span'
27719     ],
27720     
27721     onRender : function(ct, position)
27722     {
27723        // Roo.log("Call onRender: " + this.xtype);
27724         
27725        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27726        Roo.log(this.el);
27727        this.el.dom.style.marginBottom = '0';
27728        var _this = this;
27729        var editorcore = this.editorcore;
27730        var editor= this.editor;
27731        
27732        var children = [];
27733        var btn = function(id,cmd , toggle, handler, html){
27734        
27735             var  event = toggle ? 'toggle' : 'click';
27736        
27737             var a = {
27738                 size : 'sm',
27739                 xtype: 'Button',
27740                 xns: Roo.bootstrap,
27741                 //glyphicon : id,
27742                 fa: id,
27743                 cmd : id || cmd,
27744                 enableToggle:toggle !== false,
27745                 html : html || '',
27746                 pressed : toggle ? false : null,
27747                 listeners : {}
27748             };
27749             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27750                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27751             };
27752             children.push(a);
27753             return a;
27754        }
27755        
27756     //    var cb_box = function...
27757         
27758         var style = {
27759                 xtype: 'Button',
27760                 size : 'sm',
27761                 xns: Roo.bootstrap,
27762                 fa : 'font',
27763                 //html : 'submit'
27764                 menu : {
27765                     xtype: 'Menu',
27766                     xns: Roo.bootstrap,
27767                     items:  []
27768                 }
27769         };
27770         Roo.each(this.formats, function(f) {
27771             style.menu.items.push({
27772                 xtype :'MenuItem',
27773                 xns: Roo.bootstrap,
27774                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27775                 tagname : f,
27776                 listeners : {
27777                     click : function()
27778                     {
27779                         editorcore.insertTag(this.tagname);
27780                         editor.focus();
27781                     }
27782                 }
27783                 
27784             });
27785         });
27786         children.push(style);   
27787         
27788         btn('bold',false,true);
27789         btn('italic',false,true);
27790         btn('align-left', 'justifyleft',true);
27791         btn('align-center', 'justifycenter',true);
27792         btn('align-right' , 'justifyright',true);
27793         btn('link', false, false, function(btn) {
27794             //Roo.log("create link?");
27795             var url = prompt(this.createLinkText, this.defaultLinkValue);
27796             if(url && url != 'http:/'+'/'){
27797                 this.editorcore.relayCmd('createlink', url);
27798             }
27799         }),
27800         btn('list','insertunorderedlist',true);
27801         btn('pencil', false,true, function(btn){
27802                 Roo.log(this);
27803                 this.toggleSourceEdit(btn.pressed);
27804         });
27805         
27806         if (this.editor.btns.length > 0) {
27807             for (var i = 0; i<this.editor.btns.length; i++) {
27808                 children.push(this.editor.btns[i]);
27809             }
27810         }
27811         
27812         /*
27813         var cog = {
27814                 xtype: 'Button',
27815                 size : 'sm',
27816                 xns: Roo.bootstrap,
27817                 glyphicon : 'cog',
27818                 //html : 'submit'
27819                 menu : {
27820                     xtype: 'Menu',
27821                     xns: Roo.bootstrap,
27822                     items:  []
27823                 }
27824         };
27825         
27826         cog.menu.items.push({
27827             xtype :'MenuItem',
27828             xns: Roo.bootstrap,
27829             html : Clean styles,
27830             tagname : f,
27831             listeners : {
27832                 click : function()
27833                 {
27834                     editorcore.insertTag(this.tagname);
27835                     editor.focus();
27836                 }
27837             }
27838             
27839         });
27840        */
27841         
27842          
27843        this.xtype = 'NavSimplebar';
27844         
27845         for(var i=0;i< children.length;i++) {
27846             
27847             this.buttons.add(this.addxtypeChild(children[i]));
27848             
27849         }
27850         
27851         editor.on('editorevent', this.updateToolbar, this);
27852     },
27853     onBtnClick : function(id)
27854     {
27855        this.editorcore.relayCmd(id);
27856        this.editorcore.focus();
27857     },
27858     
27859     /**
27860      * Protected method that will not generally be called directly. It triggers
27861      * a toolbar update by reading the markup state of the current selection in the editor.
27862      */
27863     updateToolbar: function(){
27864
27865         if(!this.editorcore.activated){
27866             this.editor.onFirstFocus(); // is this neeed?
27867             return;
27868         }
27869
27870         var btns = this.buttons; 
27871         var doc = this.editorcore.doc;
27872         btns.get('bold').setActive(doc.queryCommandState('bold'));
27873         btns.get('italic').setActive(doc.queryCommandState('italic'));
27874         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27875         
27876         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27877         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27878         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27879         
27880         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27881         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27882          /*
27883         
27884         var ans = this.editorcore.getAllAncestors();
27885         if (this.formatCombo) {
27886             
27887             
27888             var store = this.formatCombo.store;
27889             this.formatCombo.setValue("");
27890             for (var i =0; i < ans.length;i++) {
27891                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27892                     // select it..
27893                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27894                     break;
27895                 }
27896             }
27897         }
27898         
27899         
27900         
27901         // hides menus... - so this cant be on a menu...
27902         Roo.bootstrap.MenuMgr.hideAll();
27903         */
27904         Roo.bootstrap.MenuMgr.hideAll();
27905         //this.editorsyncValue();
27906     },
27907     onFirstFocus: function() {
27908         this.buttons.each(function(item){
27909            item.enable();
27910         });
27911     },
27912     toggleSourceEdit : function(sourceEditMode){
27913         
27914           
27915         if(sourceEditMode){
27916             Roo.log("disabling buttons");
27917            this.buttons.each( function(item){
27918                 if(item.cmd != 'pencil'){
27919                     item.disable();
27920                 }
27921             });
27922           
27923         }else{
27924             Roo.log("enabling buttons");
27925             if(this.editorcore.initialized){
27926                 this.buttons.each( function(item){
27927                     item.enable();
27928                 });
27929             }
27930             
27931         }
27932         Roo.log("calling toggole on editor");
27933         // tell the editor that it's been pressed..
27934         this.editor.toggleSourceEdit(sourceEditMode);
27935        
27936     }
27937 });
27938
27939
27940
27941
27942  
27943 /*
27944  * - LGPL
27945  */
27946
27947 /**
27948  * @class Roo.bootstrap.Markdown
27949  * @extends Roo.bootstrap.TextArea
27950  * Bootstrap Showdown editable area
27951  * @cfg {string} content
27952  * 
27953  * @constructor
27954  * Create a new Showdown
27955  */
27956
27957 Roo.bootstrap.Markdown = function(config){
27958     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27959    
27960 };
27961
27962 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27963     
27964     editing :false,
27965     
27966     initEvents : function()
27967     {
27968         
27969         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27970         this.markdownEl = this.el.createChild({
27971             cls : 'roo-markdown-area'
27972         });
27973         this.inputEl().addClass('d-none');
27974         if (this.getValue() == '') {
27975             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27976             
27977         } else {
27978             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27979         }
27980         this.markdownEl.on('click', this.toggleTextEdit, this);
27981         this.on('blur', this.toggleTextEdit, this);
27982         this.on('specialkey', this.resizeTextArea, this);
27983     },
27984     
27985     toggleTextEdit : function()
27986     {
27987         var sh = this.markdownEl.getHeight();
27988         this.inputEl().addClass('d-none');
27989         this.markdownEl.addClass('d-none');
27990         if (!this.editing) {
27991             // show editor?
27992             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27993             this.inputEl().removeClass('d-none');
27994             this.inputEl().focus();
27995             this.editing = true;
27996             return;
27997         }
27998         // show showdown...
27999         this.updateMarkdown();
28000         this.markdownEl.removeClass('d-none');
28001         this.editing = false;
28002         return;
28003     },
28004     updateMarkdown : function()
28005     {
28006         if (this.getValue() == '') {
28007             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28008             return;
28009         }
28010  
28011         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28012     },
28013     
28014     resizeTextArea: function () {
28015         
28016         var sh = 100;
28017         Roo.log([sh, this.getValue().split("\n").length * 30]);
28018         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28019     },
28020     setValue : function(val)
28021     {
28022         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28023         if (!this.editing) {
28024             this.updateMarkdown();
28025         }
28026         
28027     },
28028     focus : function()
28029     {
28030         if (!this.editing) {
28031             this.toggleTextEdit();
28032         }
28033         
28034     }
28035
28036
28037 });/*
28038  * Based on:
28039  * Ext JS Library 1.1.1
28040  * Copyright(c) 2006-2007, Ext JS, LLC.
28041  *
28042  * Originally Released Under LGPL - original licence link has changed is not relivant.
28043  *
28044  * Fork - LGPL
28045  * <script type="text/javascript">
28046  */
28047  
28048 /**
28049  * @class Roo.bootstrap.PagingToolbar
28050  * @extends Roo.bootstrap.NavSimplebar
28051  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28052  * @constructor
28053  * Create a new PagingToolbar
28054  * @param {Object} config The config object
28055  * @param {Roo.data.Store} store
28056  */
28057 Roo.bootstrap.PagingToolbar = function(config)
28058 {
28059     // old args format still supported... - xtype is prefered..
28060         // created from xtype...
28061     
28062     this.ds = config.dataSource;
28063     
28064     if (config.store && !this.ds) {
28065         this.store= Roo.factory(config.store, Roo.data);
28066         this.ds = this.store;
28067         this.ds.xmodule = this.xmodule || false;
28068     }
28069     
28070     this.toolbarItems = [];
28071     if (config.items) {
28072         this.toolbarItems = config.items;
28073     }
28074     
28075     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28076     
28077     this.cursor = 0;
28078     
28079     if (this.ds) { 
28080         this.bind(this.ds);
28081     }
28082     
28083     if (Roo.bootstrap.version == 4) {
28084         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28085     } else {
28086         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28087     }
28088     
28089 };
28090
28091 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28092     /**
28093      * @cfg {Roo.data.Store} dataSource
28094      * The underlying data store providing the paged data
28095      */
28096     /**
28097      * @cfg {String/HTMLElement/Element} container
28098      * container The id or element that will contain the toolbar
28099      */
28100     /**
28101      * @cfg {Boolean} displayInfo
28102      * True to display the displayMsg (defaults to false)
28103      */
28104     /**
28105      * @cfg {Number} pageSize
28106      * The number of records to display per page (defaults to 20)
28107      */
28108     pageSize: 20,
28109     /**
28110      * @cfg {String} displayMsg
28111      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28112      */
28113     displayMsg : 'Displaying {0} - {1} of {2}',
28114     /**
28115      * @cfg {String} emptyMsg
28116      * The message to display when no records are found (defaults to "No data to display")
28117      */
28118     emptyMsg : 'No data to display',
28119     /**
28120      * Customizable piece of the default paging text (defaults to "Page")
28121      * @type String
28122      */
28123     beforePageText : "Page",
28124     /**
28125      * Customizable piece of the default paging text (defaults to "of %0")
28126      * @type String
28127      */
28128     afterPageText : "of {0}",
28129     /**
28130      * Customizable piece of the default paging text (defaults to "First Page")
28131      * @type String
28132      */
28133     firstText : "First Page",
28134     /**
28135      * Customizable piece of the default paging text (defaults to "Previous Page")
28136      * @type String
28137      */
28138     prevText : "Previous Page",
28139     /**
28140      * Customizable piece of the default paging text (defaults to "Next Page")
28141      * @type String
28142      */
28143     nextText : "Next Page",
28144     /**
28145      * Customizable piece of the default paging text (defaults to "Last Page")
28146      * @type String
28147      */
28148     lastText : "Last Page",
28149     /**
28150      * Customizable piece of the default paging text (defaults to "Refresh")
28151      * @type String
28152      */
28153     refreshText : "Refresh",
28154
28155     buttons : false,
28156     // private
28157     onRender : function(ct, position) 
28158     {
28159         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28160         this.navgroup.parentId = this.id;
28161         this.navgroup.onRender(this.el, null);
28162         // add the buttons to the navgroup
28163         
28164         if(this.displayInfo){
28165             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28166             this.displayEl = this.el.select('.x-paging-info', true).first();
28167 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28168 //            this.displayEl = navel.el.select('span',true).first();
28169         }
28170         
28171         var _this = this;
28172         
28173         if(this.buttons){
28174             Roo.each(_this.buttons, function(e){ // this might need to use render????
28175                Roo.factory(e).render(_this.el);
28176             });
28177         }
28178             
28179         Roo.each(_this.toolbarItems, function(e) {
28180             _this.navgroup.addItem(e);
28181         });
28182         
28183         
28184         this.first = this.navgroup.addItem({
28185             tooltip: this.firstText,
28186             cls: "prev btn-outline-secondary",
28187             html : ' <i class="fa fa-step-backward"></i>',
28188             disabled: true,
28189             preventDefault: true,
28190             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28191         });
28192         
28193         this.prev =  this.navgroup.addItem({
28194             tooltip: this.prevText,
28195             cls: "prev btn-outline-secondary",
28196             html : ' <i class="fa fa-backward"></i>',
28197             disabled: true,
28198             preventDefault: true,
28199             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28200         });
28201     //this.addSeparator();
28202         
28203         
28204         var field = this.navgroup.addItem( {
28205             tagtype : 'span',
28206             cls : 'x-paging-position  btn-outline-secondary',
28207              disabled: true,
28208             html : this.beforePageText  +
28209                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28210                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28211          } ); //?? escaped?
28212         
28213         this.field = field.el.select('input', true).first();
28214         this.field.on("keydown", this.onPagingKeydown, this);
28215         this.field.on("focus", function(){this.dom.select();});
28216     
28217     
28218         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28219         //this.field.setHeight(18);
28220         //this.addSeparator();
28221         this.next = this.navgroup.addItem({
28222             tooltip: this.nextText,
28223             cls: "next btn-outline-secondary",
28224             html : ' <i class="fa fa-forward"></i>',
28225             disabled: true,
28226             preventDefault: true,
28227             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28228         });
28229         this.last = this.navgroup.addItem({
28230             tooltip: this.lastText,
28231             html : ' <i class="fa fa-step-forward"></i>',
28232             cls: "next btn-outline-secondary",
28233             disabled: true,
28234             preventDefault: true,
28235             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28236         });
28237     //this.addSeparator();
28238         this.loading = this.navgroup.addItem({
28239             tooltip: this.refreshText,
28240             cls: "btn-outline-secondary",
28241             html : ' <i class="fa fa-refresh"></i>',
28242             preventDefault: true,
28243             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28244         });
28245         
28246     },
28247
28248     // private
28249     updateInfo : function(){
28250         if(this.displayEl){
28251             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28252             var msg = count == 0 ?
28253                 this.emptyMsg :
28254                 String.format(
28255                     this.displayMsg,
28256                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28257                 );
28258             this.displayEl.update(msg);
28259         }
28260     },
28261
28262     // private
28263     onLoad : function(ds, r, o)
28264     {
28265         this.cursor = o.params && o.params.start ? o.params.start : 0;
28266         
28267         var d = this.getPageData(),
28268             ap = d.activePage,
28269             ps = d.pages;
28270         
28271         
28272         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28273         this.field.dom.value = ap;
28274         this.first.setDisabled(ap == 1);
28275         this.prev.setDisabled(ap == 1);
28276         this.next.setDisabled(ap == ps);
28277         this.last.setDisabled(ap == ps);
28278         this.loading.enable();
28279         this.updateInfo();
28280     },
28281
28282     // private
28283     getPageData : function(){
28284         var total = this.ds.getTotalCount();
28285         return {
28286             total : total,
28287             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28288             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28289         };
28290     },
28291
28292     // private
28293     onLoadError : function(){
28294         this.loading.enable();
28295     },
28296
28297     // private
28298     onPagingKeydown : function(e){
28299         var k = e.getKey();
28300         var d = this.getPageData();
28301         if(k == e.RETURN){
28302             var v = this.field.dom.value, pageNum;
28303             if(!v || isNaN(pageNum = parseInt(v, 10))){
28304                 this.field.dom.value = d.activePage;
28305                 return;
28306             }
28307             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28308             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28309             e.stopEvent();
28310         }
28311         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))
28312         {
28313           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28314           this.field.dom.value = pageNum;
28315           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28316           e.stopEvent();
28317         }
28318         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28319         {
28320           var v = this.field.dom.value, pageNum; 
28321           var increment = (e.shiftKey) ? 10 : 1;
28322           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28323                 increment *= -1;
28324           }
28325           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28326             this.field.dom.value = d.activePage;
28327             return;
28328           }
28329           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28330           {
28331             this.field.dom.value = parseInt(v, 10) + increment;
28332             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28333             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28334           }
28335           e.stopEvent();
28336         }
28337     },
28338
28339     // private
28340     beforeLoad : function(){
28341         if(this.loading){
28342             this.loading.disable();
28343         }
28344     },
28345
28346     // private
28347     onClick : function(which){
28348         
28349         var ds = this.ds;
28350         if (!ds) {
28351             return;
28352         }
28353         
28354         switch(which){
28355             case "first":
28356                 ds.load({params:{start: 0, limit: this.pageSize}});
28357             break;
28358             case "prev":
28359                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28360             break;
28361             case "next":
28362                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28363             break;
28364             case "last":
28365                 var total = ds.getTotalCount();
28366                 var extra = total % this.pageSize;
28367                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28368                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28369             break;
28370             case "refresh":
28371                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28372             break;
28373         }
28374     },
28375
28376     /**
28377      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28378      * @param {Roo.data.Store} store The data store to unbind
28379      */
28380     unbind : function(ds){
28381         ds.un("beforeload", this.beforeLoad, this);
28382         ds.un("load", this.onLoad, this);
28383         ds.un("loadexception", this.onLoadError, this);
28384         ds.un("remove", this.updateInfo, this);
28385         ds.un("add", this.updateInfo, this);
28386         this.ds = undefined;
28387     },
28388
28389     /**
28390      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28391      * @param {Roo.data.Store} store The data store to bind
28392      */
28393     bind : function(ds){
28394         ds.on("beforeload", this.beforeLoad, this);
28395         ds.on("load", this.onLoad, this);
28396         ds.on("loadexception", this.onLoadError, this);
28397         ds.on("remove", this.updateInfo, this);
28398         ds.on("add", this.updateInfo, this);
28399         this.ds = ds;
28400     }
28401 });/*
28402  * - LGPL
28403  *
28404  * element
28405  * 
28406  */
28407
28408 /**
28409  * @class Roo.bootstrap.MessageBar
28410  * @extends Roo.bootstrap.Component
28411  * Bootstrap MessageBar class
28412  * @cfg {String} html contents of the MessageBar
28413  * @cfg {String} weight (info | success | warning | danger) default info
28414  * @cfg {String} beforeClass insert the bar before the given class
28415  * @cfg {Boolean} closable (true | false) default false
28416  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28417  * 
28418  * @constructor
28419  * Create a new Element
28420  * @param {Object} config The config object
28421  */
28422
28423 Roo.bootstrap.MessageBar = function(config){
28424     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28425 };
28426
28427 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28428     
28429     html: '',
28430     weight: 'info',
28431     closable: false,
28432     fixed: false,
28433     beforeClass: 'bootstrap-sticky-wrap',
28434     
28435     getAutoCreate : function(){
28436         
28437         var cfg = {
28438             tag: 'div',
28439             cls: 'alert alert-dismissable alert-' + this.weight,
28440             cn: [
28441                 {
28442                     tag: 'span',
28443                     cls: 'message',
28444                     html: this.html || ''
28445                 }
28446             ]
28447         };
28448         
28449         if(this.fixed){
28450             cfg.cls += ' alert-messages-fixed';
28451         }
28452         
28453         if(this.closable){
28454             cfg.cn.push({
28455                 tag: 'button',
28456                 cls: 'close',
28457                 html: 'x'
28458             });
28459         }
28460         
28461         return cfg;
28462     },
28463     
28464     onRender : function(ct, position)
28465     {
28466         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28467         
28468         if(!this.el){
28469             var cfg = Roo.apply({},  this.getAutoCreate());
28470             cfg.id = Roo.id();
28471             
28472             if (this.cls) {
28473                 cfg.cls += ' ' + this.cls;
28474             }
28475             if (this.style) {
28476                 cfg.style = this.style;
28477             }
28478             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28479             
28480             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28481         }
28482         
28483         this.el.select('>button.close').on('click', this.hide, this);
28484         
28485     },
28486     
28487     show : function()
28488     {
28489         if (!this.rendered) {
28490             this.render();
28491         }
28492         
28493         this.el.show();
28494         
28495         this.fireEvent('show', this);
28496         
28497     },
28498     
28499     hide : function()
28500     {
28501         if (!this.rendered) {
28502             this.render();
28503         }
28504         
28505         this.el.hide();
28506         
28507         this.fireEvent('hide', this);
28508     },
28509     
28510     update : function()
28511     {
28512 //        var e = this.el.dom.firstChild;
28513 //        
28514 //        if(this.closable){
28515 //            e = e.nextSibling;
28516 //        }
28517 //        
28518 //        e.data = this.html || '';
28519
28520         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28521     }
28522    
28523 });
28524
28525  
28526
28527      /*
28528  * - LGPL
28529  *
28530  * Graph
28531  * 
28532  */
28533
28534
28535 /**
28536  * @class Roo.bootstrap.Graph
28537  * @extends Roo.bootstrap.Component
28538  * Bootstrap Graph class
28539 > Prameters
28540  -sm {number} sm 4
28541  -md {number} md 5
28542  @cfg {String} graphtype  bar | vbar | pie
28543  @cfg {number} g_x coodinator | centre x (pie)
28544  @cfg {number} g_y coodinator | centre y (pie)
28545  @cfg {number} g_r radius (pie)
28546  @cfg {number} g_height height of the chart (respected by all elements in the set)
28547  @cfg {number} g_width width of the chart (respected by all elements in the set)
28548  @cfg {Object} title The title of the chart
28549     
28550  -{Array}  values
28551  -opts (object) options for the chart 
28552      o {
28553      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28554      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28555      o vgutter (number)
28556      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.
28557      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28558      o to
28559      o stretch (boolean)
28560      o }
28561  -opts (object) options for the pie
28562      o{
28563      o cut
28564      o startAngle (number)
28565      o endAngle (number)
28566      } 
28567  *
28568  * @constructor
28569  * Create a new Input
28570  * @param {Object} config The config object
28571  */
28572
28573 Roo.bootstrap.Graph = function(config){
28574     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28575     
28576     this.addEvents({
28577         // img events
28578         /**
28579          * @event click
28580          * The img click event for the img.
28581          * @param {Roo.EventObject} e
28582          */
28583         "click" : true
28584     });
28585 };
28586
28587 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28588     
28589     sm: 4,
28590     md: 5,
28591     graphtype: 'bar',
28592     g_height: 250,
28593     g_width: 400,
28594     g_x: 50,
28595     g_y: 50,
28596     g_r: 30,
28597     opts:{
28598         //g_colors: this.colors,
28599         g_type: 'soft',
28600         g_gutter: '20%'
28601
28602     },
28603     title : false,
28604
28605     getAutoCreate : function(){
28606         
28607         var cfg = {
28608             tag: 'div',
28609             html : null
28610         };
28611         
28612         
28613         return  cfg;
28614     },
28615
28616     onRender : function(ct,position){
28617         
28618         
28619         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28620         
28621         if (typeof(Raphael) == 'undefined') {
28622             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28623             return;
28624         }
28625         
28626         this.raphael = Raphael(this.el.dom);
28627         
28628                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28629                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28630                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28631                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28632                 /*
28633                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28634                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28635                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28636                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28637                 
28638                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28639                 r.barchart(330, 10, 300, 220, data1);
28640                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28641                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28642                 */
28643                 
28644                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28645                 // r.barchart(30, 30, 560, 250,  xdata, {
28646                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28647                 //     axis : "0 0 1 1",
28648                 //     axisxlabels :  xdata
28649                 //     //yvalues : cols,
28650                    
28651                 // });
28652 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28653 //        
28654 //        this.load(null,xdata,{
28655 //                axis : "0 0 1 1",
28656 //                axisxlabels :  xdata
28657 //                });
28658
28659     },
28660
28661     load : function(graphtype,xdata,opts)
28662     {
28663         this.raphael.clear();
28664         if(!graphtype) {
28665             graphtype = this.graphtype;
28666         }
28667         if(!opts){
28668             opts = this.opts;
28669         }
28670         var r = this.raphael,
28671             fin = function () {
28672                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28673             },
28674             fout = function () {
28675                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28676             },
28677             pfin = function() {
28678                 this.sector.stop();
28679                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28680
28681                 if (this.label) {
28682                     this.label[0].stop();
28683                     this.label[0].attr({ r: 7.5 });
28684                     this.label[1].attr({ "font-weight": 800 });
28685                 }
28686             },
28687             pfout = function() {
28688                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28689
28690                 if (this.label) {
28691                     this.label[0].animate({ r: 5 }, 500, "bounce");
28692                     this.label[1].attr({ "font-weight": 400 });
28693                 }
28694             };
28695
28696         switch(graphtype){
28697             case 'bar':
28698                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28699                 break;
28700             case 'hbar':
28701                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28702                 break;
28703             case 'pie':
28704 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28705 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28706 //            
28707                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28708                 
28709                 break;
28710
28711         }
28712         
28713         if(this.title){
28714             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28715         }
28716         
28717     },
28718     
28719     setTitle: function(o)
28720     {
28721         this.title = o;
28722     },
28723     
28724     initEvents: function() {
28725         
28726         if(!this.href){
28727             this.el.on('click', this.onClick, this);
28728         }
28729     },
28730     
28731     onClick : function(e)
28732     {
28733         Roo.log('img onclick');
28734         this.fireEvent('click', this, e);
28735     }
28736    
28737 });
28738
28739  
28740 /*
28741  * - LGPL
28742  *
28743  * numberBox
28744  * 
28745  */
28746 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28747
28748 /**
28749  * @class Roo.bootstrap.dash.NumberBox
28750  * @extends Roo.bootstrap.Component
28751  * Bootstrap NumberBox class
28752  * @cfg {String} headline Box headline
28753  * @cfg {String} content Box content
28754  * @cfg {String} icon Box icon
28755  * @cfg {String} footer Footer text
28756  * @cfg {String} fhref Footer href
28757  * 
28758  * @constructor
28759  * Create a new NumberBox
28760  * @param {Object} config The config object
28761  */
28762
28763
28764 Roo.bootstrap.dash.NumberBox = function(config){
28765     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28766     
28767 };
28768
28769 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28770     
28771     headline : '',
28772     content : '',
28773     icon : '',
28774     footer : '',
28775     fhref : '',
28776     ficon : '',
28777     
28778     getAutoCreate : function(){
28779         
28780         var cfg = {
28781             tag : 'div',
28782             cls : 'small-box ',
28783             cn : [
28784                 {
28785                     tag : 'div',
28786                     cls : 'inner',
28787                     cn :[
28788                         {
28789                             tag : 'h3',
28790                             cls : 'roo-headline',
28791                             html : this.headline
28792                         },
28793                         {
28794                             tag : 'p',
28795                             cls : 'roo-content',
28796                             html : this.content
28797                         }
28798                     ]
28799                 }
28800             ]
28801         };
28802         
28803         if(this.icon){
28804             cfg.cn.push({
28805                 tag : 'div',
28806                 cls : 'icon',
28807                 cn :[
28808                     {
28809                         tag : 'i',
28810                         cls : 'ion ' + this.icon
28811                     }
28812                 ]
28813             });
28814         }
28815         
28816         if(this.footer){
28817             var footer = {
28818                 tag : 'a',
28819                 cls : 'small-box-footer',
28820                 href : this.fhref || '#',
28821                 html : this.footer
28822             };
28823             
28824             cfg.cn.push(footer);
28825             
28826         }
28827         
28828         return  cfg;
28829     },
28830
28831     onRender : function(ct,position){
28832         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28833
28834
28835        
28836                 
28837     },
28838
28839     setHeadline: function (value)
28840     {
28841         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28842     },
28843     
28844     setFooter: function (value, href)
28845     {
28846         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28847         
28848         if(href){
28849             this.el.select('a.small-box-footer',true).first().attr('href', href);
28850         }
28851         
28852     },
28853
28854     setContent: function (value)
28855     {
28856         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28857     },
28858
28859     initEvents: function() 
28860     {   
28861         
28862     }
28863     
28864 });
28865
28866  
28867 /*
28868  * - LGPL
28869  *
28870  * TabBox
28871  * 
28872  */
28873 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28874
28875 /**
28876  * @class Roo.bootstrap.dash.TabBox
28877  * @extends Roo.bootstrap.Component
28878  * Bootstrap TabBox class
28879  * @cfg {String} title Title of the TabBox
28880  * @cfg {String} icon Icon of the TabBox
28881  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28882  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28883  * 
28884  * @constructor
28885  * Create a new TabBox
28886  * @param {Object} config The config object
28887  */
28888
28889
28890 Roo.bootstrap.dash.TabBox = function(config){
28891     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28892     this.addEvents({
28893         // raw events
28894         /**
28895          * @event addpane
28896          * When a pane is added
28897          * @param {Roo.bootstrap.dash.TabPane} pane
28898          */
28899         "addpane" : true,
28900         /**
28901          * @event activatepane
28902          * When a pane is activated
28903          * @param {Roo.bootstrap.dash.TabPane} pane
28904          */
28905         "activatepane" : true
28906         
28907          
28908     });
28909     
28910     this.panes = [];
28911 };
28912
28913 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28914
28915     title : '',
28916     icon : false,
28917     showtabs : true,
28918     tabScrollable : false,
28919     
28920     getChildContainer : function()
28921     {
28922         return this.el.select('.tab-content', true).first();
28923     },
28924     
28925     getAutoCreate : function(){
28926         
28927         var header = {
28928             tag: 'li',
28929             cls: 'pull-left header',
28930             html: this.title,
28931             cn : []
28932         };
28933         
28934         if(this.icon){
28935             header.cn.push({
28936                 tag: 'i',
28937                 cls: 'fa ' + this.icon
28938             });
28939         }
28940         
28941         var h = {
28942             tag: 'ul',
28943             cls: 'nav nav-tabs pull-right',
28944             cn: [
28945                 header
28946             ]
28947         };
28948         
28949         if(this.tabScrollable){
28950             h = {
28951                 tag: 'div',
28952                 cls: 'tab-header',
28953                 cn: [
28954                     {
28955                         tag: 'ul',
28956                         cls: 'nav nav-tabs pull-right',
28957                         cn: [
28958                             header
28959                         ]
28960                     }
28961                 ]
28962             };
28963         }
28964         
28965         var cfg = {
28966             tag: 'div',
28967             cls: 'nav-tabs-custom',
28968             cn: [
28969                 h,
28970                 {
28971                     tag: 'div',
28972                     cls: 'tab-content no-padding',
28973                     cn: []
28974                 }
28975             ]
28976         };
28977
28978         return  cfg;
28979     },
28980     initEvents : function()
28981     {
28982         //Roo.log('add add pane handler');
28983         this.on('addpane', this.onAddPane, this);
28984     },
28985      /**
28986      * Updates the box title
28987      * @param {String} html to set the title to.
28988      */
28989     setTitle : function(value)
28990     {
28991         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28992     },
28993     onAddPane : function(pane)
28994     {
28995         this.panes.push(pane);
28996         //Roo.log('addpane');
28997         //Roo.log(pane);
28998         // tabs are rendere left to right..
28999         if(!this.showtabs){
29000             return;
29001         }
29002         
29003         var ctr = this.el.select('.nav-tabs', true).first();
29004          
29005          
29006         var existing = ctr.select('.nav-tab',true);
29007         var qty = existing.getCount();;
29008         
29009         
29010         var tab = ctr.createChild({
29011             tag : 'li',
29012             cls : 'nav-tab' + (qty ? '' : ' active'),
29013             cn : [
29014                 {
29015                     tag : 'a',
29016                     href:'#',
29017                     html : pane.title
29018                 }
29019             ]
29020         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29021         pane.tab = tab;
29022         
29023         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29024         if (!qty) {
29025             pane.el.addClass('active');
29026         }
29027         
29028                 
29029     },
29030     onTabClick : function(ev,un,ob,pane)
29031     {
29032         //Roo.log('tab - prev default');
29033         ev.preventDefault();
29034         
29035         
29036         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29037         pane.tab.addClass('active');
29038         //Roo.log(pane.title);
29039         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29040         // technically we should have a deactivate event.. but maybe add later.
29041         // and it should not de-activate the selected tab...
29042         this.fireEvent('activatepane', pane);
29043         pane.el.addClass('active');
29044         pane.fireEvent('activate');
29045         
29046         
29047     },
29048     
29049     getActivePane : function()
29050     {
29051         var r = false;
29052         Roo.each(this.panes, function(p) {
29053             if(p.el.hasClass('active')){
29054                 r = p;
29055                 return false;
29056             }
29057             
29058             return;
29059         });
29060         
29061         return r;
29062     }
29063     
29064     
29065 });
29066
29067  
29068 /*
29069  * - LGPL
29070  *
29071  * Tab pane
29072  * 
29073  */
29074 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29075 /**
29076  * @class Roo.bootstrap.TabPane
29077  * @extends Roo.bootstrap.Component
29078  * Bootstrap TabPane class
29079  * @cfg {Boolean} active (false | true) Default false
29080  * @cfg {String} title title of panel
29081
29082  * 
29083  * @constructor
29084  * Create a new TabPane
29085  * @param {Object} config The config object
29086  */
29087
29088 Roo.bootstrap.dash.TabPane = function(config){
29089     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29090     
29091     this.addEvents({
29092         // raw events
29093         /**
29094          * @event activate
29095          * When a pane is activated
29096          * @param {Roo.bootstrap.dash.TabPane} pane
29097          */
29098         "activate" : true
29099          
29100     });
29101 };
29102
29103 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29104     
29105     active : false,
29106     title : '',
29107     
29108     // the tabBox that this is attached to.
29109     tab : false,
29110      
29111     getAutoCreate : function() 
29112     {
29113         var cfg = {
29114             tag: 'div',
29115             cls: 'tab-pane'
29116         };
29117         
29118         if(this.active){
29119             cfg.cls += ' active';
29120         }
29121         
29122         return cfg;
29123     },
29124     initEvents  : function()
29125     {
29126         //Roo.log('trigger add pane handler');
29127         this.parent().fireEvent('addpane', this)
29128     },
29129     
29130      /**
29131      * Updates the tab title 
29132      * @param {String} html to set the title to.
29133      */
29134     setTitle: function(str)
29135     {
29136         if (!this.tab) {
29137             return;
29138         }
29139         this.title = str;
29140         this.tab.select('a', true).first().dom.innerHTML = str;
29141         
29142     }
29143     
29144     
29145     
29146 });
29147
29148  
29149
29150
29151  /*
29152  * - LGPL
29153  *
29154  * menu
29155  * 
29156  */
29157 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29158
29159 /**
29160  * @class Roo.bootstrap.menu.Menu
29161  * @extends Roo.bootstrap.Component
29162  * Bootstrap Menu class - container for Menu
29163  * @cfg {String} html Text of the menu
29164  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29165  * @cfg {String} icon Font awesome icon
29166  * @cfg {String} pos Menu align to (top | bottom) default bottom
29167  * 
29168  * 
29169  * @constructor
29170  * Create a new Menu
29171  * @param {Object} config The config object
29172  */
29173
29174
29175 Roo.bootstrap.menu.Menu = function(config){
29176     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29177     
29178     this.addEvents({
29179         /**
29180          * @event beforeshow
29181          * Fires before this menu is displayed
29182          * @param {Roo.bootstrap.menu.Menu} this
29183          */
29184         beforeshow : true,
29185         /**
29186          * @event beforehide
29187          * Fires before this menu is hidden
29188          * @param {Roo.bootstrap.menu.Menu} this
29189          */
29190         beforehide : true,
29191         /**
29192          * @event show
29193          * Fires after this menu is displayed
29194          * @param {Roo.bootstrap.menu.Menu} this
29195          */
29196         show : true,
29197         /**
29198          * @event hide
29199          * Fires after this menu is hidden
29200          * @param {Roo.bootstrap.menu.Menu} this
29201          */
29202         hide : true,
29203         /**
29204          * @event click
29205          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29206          * @param {Roo.bootstrap.menu.Menu} this
29207          * @param {Roo.EventObject} e
29208          */
29209         click : true
29210     });
29211     
29212 };
29213
29214 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29215     
29216     submenu : false,
29217     html : '',
29218     weight : 'default',
29219     icon : false,
29220     pos : 'bottom',
29221     
29222     
29223     getChildContainer : function() {
29224         if(this.isSubMenu){
29225             return this.el;
29226         }
29227         
29228         return this.el.select('ul.dropdown-menu', true).first();  
29229     },
29230     
29231     getAutoCreate : function()
29232     {
29233         var text = [
29234             {
29235                 tag : 'span',
29236                 cls : 'roo-menu-text',
29237                 html : this.html
29238             }
29239         ];
29240         
29241         if(this.icon){
29242             text.unshift({
29243                 tag : 'i',
29244                 cls : 'fa ' + this.icon
29245             })
29246         }
29247         
29248         
29249         var cfg = {
29250             tag : 'div',
29251             cls : 'btn-group',
29252             cn : [
29253                 {
29254                     tag : 'button',
29255                     cls : 'dropdown-button btn btn-' + this.weight,
29256                     cn : text
29257                 },
29258                 {
29259                     tag : 'button',
29260                     cls : 'dropdown-toggle btn btn-' + this.weight,
29261                     cn : [
29262                         {
29263                             tag : 'span',
29264                             cls : 'caret'
29265                         }
29266                     ]
29267                 },
29268                 {
29269                     tag : 'ul',
29270                     cls : 'dropdown-menu'
29271                 }
29272             ]
29273             
29274         };
29275         
29276         if(this.pos == 'top'){
29277             cfg.cls += ' dropup';
29278         }
29279         
29280         if(this.isSubMenu){
29281             cfg = {
29282                 tag : 'ul',
29283                 cls : 'dropdown-menu'
29284             }
29285         }
29286         
29287         return cfg;
29288     },
29289     
29290     onRender : function(ct, position)
29291     {
29292         this.isSubMenu = ct.hasClass('dropdown-submenu');
29293         
29294         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29295     },
29296     
29297     initEvents : function() 
29298     {
29299         if(this.isSubMenu){
29300             return;
29301         }
29302         
29303         this.hidden = true;
29304         
29305         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29306         this.triggerEl.on('click', this.onTriggerPress, this);
29307         
29308         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29309         this.buttonEl.on('click', this.onClick, this);
29310         
29311     },
29312     
29313     list : function()
29314     {
29315         if(this.isSubMenu){
29316             return this.el;
29317         }
29318         
29319         return this.el.select('ul.dropdown-menu', true).first();
29320     },
29321     
29322     onClick : function(e)
29323     {
29324         this.fireEvent("click", this, e);
29325     },
29326     
29327     onTriggerPress  : function(e)
29328     {   
29329         if (this.isVisible()) {
29330             this.hide();
29331         } else {
29332             this.show();
29333         }
29334     },
29335     
29336     isVisible : function(){
29337         return !this.hidden;
29338     },
29339     
29340     show : function()
29341     {
29342         this.fireEvent("beforeshow", this);
29343         
29344         this.hidden = false;
29345         this.el.addClass('open');
29346         
29347         Roo.get(document).on("mouseup", this.onMouseUp, this);
29348         
29349         this.fireEvent("show", this);
29350         
29351         
29352     },
29353     
29354     hide : function()
29355     {
29356         this.fireEvent("beforehide", this);
29357         
29358         this.hidden = true;
29359         this.el.removeClass('open');
29360         
29361         Roo.get(document).un("mouseup", this.onMouseUp);
29362         
29363         this.fireEvent("hide", this);
29364     },
29365     
29366     onMouseUp : function()
29367     {
29368         this.hide();
29369     }
29370     
29371 });
29372
29373  
29374  /*
29375  * - LGPL
29376  *
29377  * menu item
29378  * 
29379  */
29380 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29381
29382 /**
29383  * @class Roo.bootstrap.menu.Item
29384  * @extends Roo.bootstrap.Component
29385  * Bootstrap MenuItem class
29386  * @cfg {Boolean} submenu (true | false) default false
29387  * @cfg {String} html text of the item
29388  * @cfg {String} href the link
29389  * @cfg {Boolean} disable (true | false) default false
29390  * @cfg {Boolean} preventDefault (true | false) default true
29391  * @cfg {String} icon Font awesome icon
29392  * @cfg {String} pos Submenu align to (left | right) default right 
29393  * 
29394  * 
29395  * @constructor
29396  * Create a new Item
29397  * @param {Object} config The config object
29398  */
29399
29400
29401 Roo.bootstrap.menu.Item = function(config){
29402     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29403     this.addEvents({
29404         /**
29405          * @event mouseover
29406          * Fires when the mouse is hovering over this menu
29407          * @param {Roo.bootstrap.menu.Item} this
29408          * @param {Roo.EventObject} e
29409          */
29410         mouseover : true,
29411         /**
29412          * @event mouseout
29413          * Fires when the mouse exits this menu
29414          * @param {Roo.bootstrap.menu.Item} this
29415          * @param {Roo.EventObject} e
29416          */
29417         mouseout : true,
29418         // raw events
29419         /**
29420          * @event click
29421          * The raw click event for the entire grid.
29422          * @param {Roo.EventObject} e
29423          */
29424         click : true
29425     });
29426 };
29427
29428 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29429     
29430     submenu : false,
29431     href : '',
29432     html : '',
29433     preventDefault: true,
29434     disable : false,
29435     icon : false,
29436     pos : 'right',
29437     
29438     getAutoCreate : function()
29439     {
29440         var text = [
29441             {
29442                 tag : 'span',
29443                 cls : 'roo-menu-item-text',
29444                 html : this.html
29445             }
29446         ];
29447         
29448         if(this.icon){
29449             text.unshift({
29450                 tag : 'i',
29451                 cls : 'fa ' + this.icon
29452             })
29453         }
29454         
29455         var cfg = {
29456             tag : 'li',
29457             cn : [
29458                 {
29459                     tag : 'a',
29460                     href : this.href || '#',
29461                     cn : text
29462                 }
29463             ]
29464         };
29465         
29466         if(this.disable){
29467             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29468         }
29469         
29470         if(this.submenu){
29471             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29472             
29473             if(this.pos == 'left'){
29474                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29475             }
29476         }
29477         
29478         return cfg;
29479     },
29480     
29481     initEvents : function() 
29482     {
29483         this.el.on('mouseover', this.onMouseOver, this);
29484         this.el.on('mouseout', this.onMouseOut, this);
29485         
29486         this.el.select('a', true).first().on('click', this.onClick, this);
29487         
29488     },
29489     
29490     onClick : function(e)
29491     {
29492         if(this.preventDefault){
29493             e.preventDefault();
29494         }
29495         
29496         this.fireEvent("click", this, e);
29497     },
29498     
29499     onMouseOver : function(e)
29500     {
29501         if(this.submenu && this.pos == 'left'){
29502             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29503         }
29504         
29505         this.fireEvent("mouseover", this, e);
29506     },
29507     
29508     onMouseOut : function(e)
29509     {
29510         this.fireEvent("mouseout", this, e);
29511     }
29512 });
29513
29514  
29515
29516  /*
29517  * - LGPL
29518  *
29519  * menu separator
29520  * 
29521  */
29522 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29523
29524 /**
29525  * @class Roo.bootstrap.menu.Separator
29526  * @extends Roo.bootstrap.Component
29527  * Bootstrap Separator class
29528  * 
29529  * @constructor
29530  * Create a new Separator
29531  * @param {Object} config The config object
29532  */
29533
29534
29535 Roo.bootstrap.menu.Separator = function(config){
29536     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29537 };
29538
29539 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29540     
29541     getAutoCreate : function(){
29542         var cfg = {
29543             tag : 'li',
29544             cls: 'dropdown-divider divider'
29545         };
29546         
29547         return cfg;
29548     }
29549    
29550 });
29551
29552  
29553
29554  /*
29555  * - LGPL
29556  *
29557  * Tooltip
29558  * 
29559  */
29560
29561 /**
29562  * @class Roo.bootstrap.Tooltip
29563  * Bootstrap Tooltip class
29564  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29565  * to determine which dom element triggers the tooltip.
29566  * 
29567  * It needs to add support for additional attributes like tooltip-position
29568  * 
29569  * @constructor
29570  * Create a new Toolti
29571  * @param {Object} config The config object
29572  */
29573
29574 Roo.bootstrap.Tooltip = function(config){
29575     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29576     
29577     this.alignment = Roo.bootstrap.Tooltip.alignment;
29578     
29579     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29580         this.alignment = config.alignment;
29581     }
29582     
29583 };
29584
29585 Roo.apply(Roo.bootstrap.Tooltip, {
29586     /**
29587      * @function init initialize tooltip monitoring.
29588      * @static
29589      */
29590     currentEl : false,
29591     currentTip : false,
29592     currentRegion : false,
29593     
29594     //  init : delay?
29595     
29596     init : function()
29597     {
29598         Roo.get(document).on('mouseover', this.enter ,this);
29599         Roo.get(document).on('mouseout', this.leave, this);
29600          
29601         
29602         this.currentTip = new Roo.bootstrap.Tooltip();
29603     },
29604     
29605     enter : function(ev)
29606     {
29607         var dom = ev.getTarget();
29608         
29609         //Roo.log(['enter',dom]);
29610         var el = Roo.fly(dom);
29611         if (this.currentEl) {
29612             //Roo.log(dom);
29613             //Roo.log(this.currentEl);
29614             //Roo.log(this.currentEl.contains(dom));
29615             if (this.currentEl == el) {
29616                 return;
29617             }
29618             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29619                 return;
29620             }
29621
29622         }
29623         
29624         if (this.currentTip.el) {
29625             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29626         }    
29627         //Roo.log(ev);
29628         
29629         if(!el || el.dom == document){
29630             return;
29631         }
29632         
29633         var bindEl = el; 
29634         var pel = false;
29635         if (!el.attr('tooltip')) {
29636             pel = el.findParent("[tooltip]");
29637             if (pel) {
29638                 bindEl = Roo.get(pel);
29639             }
29640         }
29641         
29642        
29643         
29644         // you can not look for children, as if el is the body.. then everythign is the child..
29645         if (!pel && !el.attr('tooltip')) { //
29646             if (!el.select("[tooltip]").elements.length) {
29647                 return;
29648             }
29649             // is the mouse over this child...?
29650             bindEl = el.select("[tooltip]").first();
29651             var xy = ev.getXY();
29652             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29653                 //Roo.log("not in region.");
29654                 return;
29655             }
29656             //Roo.log("child element over..");
29657             
29658         }
29659         this.currentEl = el;
29660         this.currentTip.bind(bindEl);
29661         this.currentRegion = Roo.lib.Region.getRegion(dom);
29662         this.currentTip.enter();
29663         
29664     },
29665     leave : function(ev)
29666     {
29667         var dom = ev.getTarget();
29668         //Roo.log(['leave',dom]);
29669         if (!this.currentEl) {
29670             return;
29671         }
29672         
29673         
29674         if (dom != this.currentEl.dom) {
29675             return;
29676         }
29677         var xy = ev.getXY();
29678         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29679             return;
29680         }
29681         // only activate leave if mouse cursor is outside... bounding box..
29682         
29683         
29684         
29685         
29686         if (this.currentTip) {
29687             this.currentTip.leave();
29688         }
29689         //Roo.log('clear currentEl');
29690         this.currentEl = false;
29691         
29692         
29693     },
29694     alignment : {
29695         'left' : ['r-l', [-2,0], 'right'],
29696         'right' : ['l-r', [2,0], 'left'],
29697         'bottom' : ['t-b', [0,2], 'top'],
29698         'top' : [ 'b-t', [0,-2], 'bottom']
29699     }
29700     
29701 });
29702
29703
29704 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29705     
29706     
29707     bindEl : false,
29708     
29709     delay : null, // can be { show : 300 , hide: 500}
29710     
29711     timeout : null,
29712     
29713     hoverState : null, //???
29714     
29715     placement : 'bottom', 
29716     
29717     alignment : false,
29718     
29719     getAutoCreate : function(){
29720     
29721         var cfg = {
29722            cls : 'tooltip',   
29723            role : 'tooltip',
29724            cn : [
29725                 {
29726                     cls : 'tooltip-arrow arrow'
29727                 },
29728                 {
29729                     cls : 'tooltip-inner'
29730                 }
29731            ]
29732         };
29733         
29734         return cfg;
29735     },
29736     bind : function(el)
29737     {
29738         this.bindEl = el;
29739     },
29740     
29741     initEvents : function()
29742     {
29743         this.arrowEl = this.el.select('.arrow', true).first();
29744         this.innerEl = this.el.select('.tooltip-inner', true).first();
29745     },
29746     
29747     enter : function () {
29748        
29749         if (this.timeout != null) {
29750             clearTimeout(this.timeout);
29751         }
29752         
29753         this.hoverState = 'in';
29754          //Roo.log("enter - show");
29755         if (!this.delay || !this.delay.show) {
29756             this.show();
29757             return;
29758         }
29759         var _t = this;
29760         this.timeout = setTimeout(function () {
29761             if (_t.hoverState == 'in') {
29762                 _t.show();
29763             }
29764         }, this.delay.show);
29765     },
29766     leave : function()
29767     {
29768         clearTimeout(this.timeout);
29769     
29770         this.hoverState = 'out';
29771          if (!this.delay || !this.delay.hide) {
29772             this.hide();
29773             return;
29774         }
29775        
29776         var _t = this;
29777         this.timeout = setTimeout(function () {
29778             //Roo.log("leave - timeout");
29779             
29780             if (_t.hoverState == 'out') {
29781                 _t.hide();
29782                 Roo.bootstrap.Tooltip.currentEl = false;
29783             }
29784         }, delay);
29785     },
29786     
29787     show : function (msg)
29788     {
29789         if (!this.el) {
29790             this.render(document.body);
29791         }
29792         // set content.
29793         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29794         
29795         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29796         
29797         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29798         
29799         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29800                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29801         
29802         var placement = typeof this.placement == 'function' ?
29803             this.placement.call(this, this.el, on_el) :
29804             this.placement;
29805             
29806         var autoToken = /\s?auto?\s?/i;
29807         var autoPlace = autoToken.test(placement);
29808         if (autoPlace) {
29809             placement = placement.replace(autoToken, '') || 'top';
29810         }
29811         
29812         //this.el.detach()
29813         //this.el.setXY([0,0]);
29814         this.el.show();
29815         //this.el.dom.style.display='block';
29816         
29817         //this.el.appendTo(on_el);
29818         
29819         var p = this.getPosition();
29820         var box = this.el.getBox();
29821         
29822         if (autoPlace) {
29823             // fixme..
29824         }
29825         
29826         var align = this.alignment[placement];
29827         
29828         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29829         
29830         if(placement == 'top' || placement == 'bottom'){
29831             if(xy[0] < 0){
29832                 placement = 'right';
29833             }
29834             
29835             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29836                 placement = 'left';
29837             }
29838             
29839             var scroll = Roo.select('body', true).first().getScroll();
29840             
29841             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29842                 placement = 'top';
29843             }
29844             
29845             align = this.alignment[placement];
29846             
29847             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29848             
29849         }
29850         
29851         var elems = document.getElementsByTagName('div');
29852         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29853         for (var i = 0; i < elems.length; i++) {
29854           var zindex = Number.parseInt(
29855                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29856                 10
29857           );
29858           if (zindex > highest) {
29859             highest = zindex;
29860           }
29861         }
29862         
29863         
29864         
29865         this.el.dom.style.zIndex = highest;
29866         
29867         this.el.alignTo(this.bindEl, align[0],align[1]);
29868         //var arrow = this.el.select('.arrow',true).first();
29869         //arrow.set(align[2], 
29870         
29871         this.el.addClass(placement);
29872         this.el.addClass("bs-tooltip-"+ placement);
29873         
29874         this.el.addClass('in fade show');
29875         
29876         this.hoverState = null;
29877         
29878         if (this.el.hasClass('fade')) {
29879             // fade it?
29880         }
29881         
29882         
29883         
29884         
29885         
29886     },
29887     hide : function()
29888     {
29889          
29890         if (!this.el) {
29891             return;
29892         }
29893         //this.el.setXY([0,0]);
29894         this.el.removeClass(['show', 'in']);
29895         //this.el.hide();
29896         
29897     }
29898     
29899 });
29900  
29901
29902  /*
29903  * - LGPL
29904  *
29905  * Location Picker
29906  * 
29907  */
29908
29909 /**
29910  * @class Roo.bootstrap.LocationPicker
29911  * @extends Roo.bootstrap.Component
29912  * Bootstrap LocationPicker class
29913  * @cfg {Number} latitude Position when init default 0
29914  * @cfg {Number} longitude Position when init default 0
29915  * @cfg {Number} zoom default 15
29916  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29917  * @cfg {Boolean} mapTypeControl default false
29918  * @cfg {Boolean} disableDoubleClickZoom default false
29919  * @cfg {Boolean} scrollwheel default true
29920  * @cfg {Boolean} streetViewControl default false
29921  * @cfg {Number} radius default 0
29922  * @cfg {String} locationName
29923  * @cfg {Boolean} draggable default true
29924  * @cfg {Boolean} enableAutocomplete default false
29925  * @cfg {Boolean} enableReverseGeocode default true
29926  * @cfg {String} markerTitle
29927  * 
29928  * @constructor
29929  * Create a new LocationPicker
29930  * @param {Object} config The config object
29931  */
29932
29933
29934 Roo.bootstrap.LocationPicker = function(config){
29935     
29936     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29937     
29938     this.addEvents({
29939         /**
29940          * @event initial
29941          * Fires when the picker initialized.
29942          * @param {Roo.bootstrap.LocationPicker} this
29943          * @param {Google Location} location
29944          */
29945         initial : true,
29946         /**
29947          * @event positionchanged
29948          * Fires when the picker position changed.
29949          * @param {Roo.bootstrap.LocationPicker} this
29950          * @param {Google Location} location
29951          */
29952         positionchanged : true,
29953         /**
29954          * @event resize
29955          * Fires when the map resize.
29956          * @param {Roo.bootstrap.LocationPicker} this
29957          */
29958         resize : true,
29959         /**
29960          * @event show
29961          * Fires when the map show.
29962          * @param {Roo.bootstrap.LocationPicker} this
29963          */
29964         show : true,
29965         /**
29966          * @event hide
29967          * Fires when the map hide.
29968          * @param {Roo.bootstrap.LocationPicker} this
29969          */
29970         hide : true,
29971         /**
29972          * @event mapClick
29973          * Fires when click the map.
29974          * @param {Roo.bootstrap.LocationPicker} this
29975          * @param {Map event} e
29976          */
29977         mapClick : true,
29978         /**
29979          * @event mapRightClick
29980          * Fires when right click the map.
29981          * @param {Roo.bootstrap.LocationPicker} this
29982          * @param {Map event} e
29983          */
29984         mapRightClick : true,
29985         /**
29986          * @event markerClick
29987          * Fires when click the marker.
29988          * @param {Roo.bootstrap.LocationPicker} this
29989          * @param {Map event} e
29990          */
29991         markerClick : true,
29992         /**
29993          * @event markerRightClick
29994          * Fires when right click the marker.
29995          * @param {Roo.bootstrap.LocationPicker} this
29996          * @param {Map event} e
29997          */
29998         markerRightClick : true,
29999         /**
30000          * @event OverlayViewDraw
30001          * Fires when OverlayView Draw
30002          * @param {Roo.bootstrap.LocationPicker} this
30003          */
30004         OverlayViewDraw : true,
30005         /**
30006          * @event OverlayViewOnAdd
30007          * Fires when OverlayView Draw
30008          * @param {Roo.bootstrap.LocationPicker} this
30009          */
30010         OverlayViewOnAdd : true,
30011         /**
30012          * @event OverlayViewOnRemove
30013          * Fires when OverlayView Draw
30014          * @param {Roo.bootstrap.LocationPicker} this
30015          */
30016         OverlayViewOnRemove : true,
30017         /**
30018          * @event OverlayViewShow
30019          * Fires when OverlayView Draw
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          * @param {Pixel} cpx
30022          */
30023         OverlayViewShow : true,
30024         /**
30025          * @event OverlayViewHide
30026          * Fires when OverlayView Draw
30027          * @param {Roo.bootstrap.LocationPicker} this
30028          */
30029         OverlayViewHide : true,
30030         /**
30031          * @event loadexception
30032          * Fires when load google lib failed.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          */
30035         loadexception : true
30036     });
30037         
30038 };
30039
30040 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30041     
30042     gMapContext: false,
30043     
30044     latitude: 0,
30045     longitude: 0,
30046     zoom: 15,
30047     mapTypeId: false,
30048     mapTypeControl: false,
30049     disableDoubleClickZoom: false,
30050     scrollwheel: true,
30051     streetViewControl: false,
30052     radius: 0,
30053     locationName: '',
30054     draggable: true,
30055     enableAutocomplete: false,
30056     enableReverseGeocode: true,
30057     markerTitle: '',
30058     
30059     getAutoCreate: function()
30060     {
30061
30062         var cfg = {
30063             tag: 'div',
30064             cls: 'roo-location-picker'
30065         };
30066         
30067         return cfg
30068     },
30069     
30070     initEvents: function(ct, position)
30071     {       
30072         if(!this.el.getWidth() || this.isApplied()){
30073             return;
30074         }
30075         
30076         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30077         
30078         this.initial();
30079     },
30080     
30081     initial: function()
30082     {
30083         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30084             this.fireEvent('loadexception', this);
30085             return;
30086         }
30087         
30088         if(!this.mapTypeId){
30089             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30090         }
30091         
30092         this.gMapContext = this.GMapContext();
30093         
30094         this.initOverlayView();
30095         
30096         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30097         
30098         var _this = this;
30099                 
30100         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30101             _this.setPosition(_this.gMapContext.marker.position);
30102         });
30103         
30104         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30105             _this.fireEvent('mapClick', this, event);
30106             
30107         });
30108
30109         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30110             _this.fireEvent('mapRightClick', this, event);
30111             
30112         });
30113         
30114         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30115             _this.fireEvent('markerClick', this, event);
30116             
30117         });
30118
30119         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30120             _this.fireEvent('markerRightClick', this, event);
30121             
30122         });
30123         
30124         this.setPosition(this.gMapContext.location);
30125         
30126         this.fireEvent('initial', this, this.gMapContext.location);
30127     },
30128     
30129     initOverlayView: function()
30130     {
30131         var _this = this;
30132         
30133         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30134             
30135             draw: function()
30136             {
30137                 _this.fireEvent('OverlayViewDraw', _this);
30138             },
30139             
30140             onAdd: function()
30141             {
30142                 _this.fireEvent('OverlayViewOnAdd', _this);
30143             },
30144             
30145             onRemove: function()
30146             {
30147                 _this.fireEvent('OverlayViewOnRemove', _this);
30148             },
30149             
30150             show: function(cpx)
30151             {
30152                 _this.fireEvent('OverlayViewShow', _this, cpx);
30153             },
30154             
30155             hide: function()
30156             {
30157                 _this.fireEvent('OverlayViewHide', _this);
30158             }
30159             
30160         });
30161     },
30162     
30163     fromLatLngToContainerPixel: function(event)
30164     {
30165         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30166     },
30167     
30168     isApplied: function() 
30169     {
30170         return this.getGmapContext() == false ? false : true;
30171     },
30172     
30173     getGmapContext: function() 
30174     {
30175         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30176     },
30177     
30178     GMapContext: function() 
30179     {
30180         var position = new google.maps.LatLng(this.latitude, this.longitude);
30181         
30182         var _map = new google.maps.Map(this.el.dom, {
30183             center: position,
30184             zoom: this.zoom,
30185             mapTypeId: this.mapTypeId,
30186             mapTypeControl: this.mapTypeControl,
30187             disableDoubleClickZoom: this.disableDoubleClickZoom,
30188             scrollwheel: this.scrollwheel,
30189             streetViewControl: this.streetViewControl,
30190             locationName: this.locationName,
30191             draggable: this.draggable,
30192             enableAutocomplete: this.enableAutocomplete,
30193             enableReverseGeocode: this.enableReverseGeocode
30194         });
30195         
30196         var _marker = new google.maps.Marker({
30197             position: position,
30198             map: _map,
30199             title: this.markerTitle,
30200             draggable: this.draggable
30201         });
30202         
30203         return {
30204             map: _map,
30205             marker: _marker,
30206             circle: null,
30207             location: position,
30208             radius: this.radius,
30209             locationName: this.locationName,
30210             addressComponents: {
30211                 formatted_address: null,
30212                 addressLine1: null,
30213                 addressLine2: null,
30214                 streetName: null,
30215                 streetNumber: null,
30216                 city: null,
30217                 district: null,
30218                 state: null,
30219                 stateOrProvince: null
30220             },
30221             settings: this,
30222             domContainer: this.el.dom,
30223             geodecoder: new google.maps.Geocoder()
30224         };
30225     },
30226     
30227     drawCircle: function(center, radius, options) 
30228     {
30229         if (this.gMapContext.circle != null) {
30230             this.gMapContext.circle.setMap(null);
30231         }
30232         if (radius > 0) {
30233             radius *= 1;
30234             options = Roo.apply({}, options, {
30235                 strokeColor: "#0000FF",
30236                 strokeOpacity: .35,
30237                 strokeWeight: 2,
30238                 fillColor: "#0000FF",
30239                 fillOpacity: .2
30240             });
30241             
30242             options.map = this.gMapContext.map;
30243             options.radius = radius;
30244             options.center = center;
30245             this.gMapContext.circle = new google.maps.Circle(options);
30246             return this.gMapContext.circle;
30247         }
30248         
30249         return null;
30250     },
30251     
30252     setPosition: function(location) 
30253     {
30254         this.gMapContext.location = location;
30255         this.gMapContext.marker.setPosition(location);
30256         this.gMapContext.map.panTo(location);
30257         this.drawCircle(location, this.gMapContext.radius, {});
30258         
30259         var _this = this;
30260         
30261         if (this.gMapContext.settings.enableReverseGeocode) {
30262             this.gMapContext.geodecoder.geocode({
30263                 latLng: this.gMapContext.location
30264             }, function(results, status) {
30265                 
30266                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30267                     _this.gMapContext.locationName = results[0].formatted_address;
30268                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30269                     
30270                     _this.fireEvent('positionchanged', this, location);
30271                 }
30272             });
30273             
30274             return;
30275         }
30276         
30277         this.fireEvent('positionchanged', this, location);
30278     },
30279     
30280     resize: function()
30281     {
30282         google.maps.event.trigger(this.gMapContext.map, "resize");
30283         
30284         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30285         
30286         this.fireEvent('resize', this);
30287     },
30288     
30289     setPositionByLatLng: function(latitude, longitude)
30290     {
30291         this.setPosition(new google.maps.LatLng(latitude, longitude));
30292     },
30293     
30294     getCurrentPosition: function() 
30295     {
30296         return {
30297             latitude: this.gMapContext.location.lat(),
30298             longitude: this.gMapContext.location.lng()
30299         };
30300     },
30301     
30302     getAddressName: function() 
30303     {
30304         return this.gMapContext.locationName;
30305     },
30306     
30307     getAddressComponents: function() 
30308     {
30309         return this.gMapContext.addressComponents;
30310     },
30311     
30312     address_component_from_google_geocode: function(address_components) 
30313     {
30314         var result = {};
30315         
30316         for (var i = 0; i < address_components.length; i++) {
30317             var component = address_components[i];
30318             if (component.types.indexOf("postal_code") >= 0) {
30319                 result.postalCode = component.short_name;
30320             } else if (component.types.indexOf("street_number") >= 0) {
30321                 result.streetNumber = component.short_name;
30322             } else if (component.types.indexOf("route") >= 0) {
30323                 result.streetName = component.short_name;
30324             } else if (component.types.indexOf("neighborhood") >= 0) {
30325                 result.city = component.short_name;
30326             } else if (component.types.indexOf("locality") >= 0) {
30327                 result.city = component.short_name;
30328             } else if (component.types.indexOf("sublocality") >= 0) {
30329                 result.district = component.short_name;
30330             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30331                 result.stateOrProvince = component.short_name;
30332             } else if (component.types.indexOf("country") >= 0) {
30333                 result.country = component.short_name;
30334             }
30335         }
30336         
30337         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30338         result.addressLine2 = "";
30339         return result;
30340     },
30341     
30342     setZoomLevel: function(zoom)
30343     {
30344         this.gMapContext.map.setZoom(zoom);
30345     },
30346     
30347     show: function()
30348     {
30349         if(!this.el){
30350             return;
30351         }
30352         
30353         this.el.show();
30354         
30355         this.resize();
30356         
30357         this.fireEvent('show', this);
30358     },
30359     
30360     hide: function()
30361     {
30362         if(!this.el){
30363             return;
30364         }
30365         
30366         this.el.hide();
30367         
30368         this.fireEvent('hide', this);
30369     }
30370     
30371 });
30372
30373 Roo.apply(Roo.bootstrap.LocationPicker, {
30374     
30375     OverlayView : function(map, options)
30376     {
30377         options = options || {};
30378         
30379         this.setMap(map);
30380     }
30381     
30382     
30383 });/**
30384  * @class Roo.bootstrap.Alert
30385  * @extends Roo.bootstrap.Component
30386  * Bootstrap Alert class - shows an alert area box
30387  * eg
30388  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30389   Enter a valid email address
30390 </div>
30391  * @licence LGPL
30392  * @cfg {String} title The title of alert
30393  * @cfg {String} html The content of alert
30394  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30395  * @cfg {String} fa font-awesomeicon
30396  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30397  * @cfg {Boolean} close true to show a x closer
30398  * 
30399  * 
30400  * @constructor
30401  * Create a new alert
30402  * @param {Object} config The config object
30403  */
30404
30405
30406 Roo.bootstrap.Alert = function(config){
30407     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30408     
30409 };
30410
30411 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30412     
30413     title: '',
30414     html: '',
30415     weight: false,
30416     fa: false,
30417     faicon: false, // BC
30418     close : false,
30419     
30420     
30421     getAutoCreate : function()
30422     {
30423         
30424         var cfg = {
30425             tag : 'div',
30426             cls : 'alert',
30427             cn : [
30428                 {
30429                     tag: 'button',
30430                     type :  "button",
30431                     cls: "close",
30432                     html : '×',
30433                     style : this.close ? '' : 'display:none'
30434                 },
30435                 {
30436                     tag : 'i',
30437                     cls : 'roo-alert-icon'
30438                     
30439                 },
30440                 {
30441                     tag : 'b',
30442                     cls : 'roo-alert-title',
30443                     html : this.title
30444                 },
30445                 {
30446                     tag : 'span',
30447                     cls : 'roo-alert-text',
30448                     html : this.html
30449                 }
30450             ]
30451         };
30452         
30453         if(this.faicon){
30454             cfg.cn[0].cls += ' fa ' + this.faicon;
30455         }
30456         if(this.fa){
30457             cfg.cn[0].cls += ' fa ' + this.fa;
30458         }
30459         
30460         if(this.weight){
30461             cfg.cls += ' alert-' + this.weight;
30462         }
30463         
30464         return cfg;
30465     },
30466     
30467     initEvents: function() 
30468     {
30469         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30470         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30471         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30472         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30473         if (this.seconds > 0) {
30474             this.hide.defer(this.seconds, this);
30475         }
30476     },
30477     /**
30478      * Set the Title Message HTML
30479      * @param {String} html
30480      */
30481     setTitle : function(str)
30482     {
30483         this.titleEl.dom.innerHTML = str;
30484     },
30485      
30486      /**
30487      * Set the Body Message HTML
30488      * @param {String} html
30489      */
30490     setHtml : function(str)
30491     {
30492         this.htmlEl.dom.innerHTML = str;
30493     },
30494     /**
30495      * Set the Weight of the alert
30496      * @param {String} (success|info|warning|danger) weight
30497      */
30498     
30499     setWeight : function(weight)
30500     {
30501         if(this.weight){
30502             this.el.removeClass('alert-' + this.weight);
30503         }
30504         
30505         this.weight = weight;
30506         
30507         this.el.addClass('alert-' + this.weight);
30508     },
30509       /**
30510      * Set the Icon of the alert
30511      * @param {String} see fontawsome names (name without the 'fa-' bit)
30512      */
30513     setIcon : function(icon)
30514     {
30515         if(this.faicon){
30516             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30517         }
30518         
30519         this.faicon = icon;
30520         
30521         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30522     },
30523     /**
30524      * Hide the Alert
30525      */
30526     hide: function() 
30527     {
30528         this.el.hide();   
30529     },
30530     /**
30531      * Show the Alert
30532      */
30533     show: function() 
30534     {  
30535         this.el.show();   
30536     }
30537     
30538 });
30539
30540  
30541 /*
30542 * Licence: LGPL
30543 */
30544
30545 /**
30546  * @class Roo.bootstrap.UploadCropbox
30547  * @extends Roo.bootstrap.Component
30548  * Bootstrap UploadCropbox class
30549  * @cfg {String} emptyText show when image has been loaded
30550  * @cfg {String} rotateNotify show when image too small to rotate
30551  * @cfg {Number} errorTimeout default 3000
30552  * @cfg {Number} minWidth default 300
30553  * @cfg {Number} minHeight default 300
30554  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30555  * @cfg {Boolean} isDocument (true|false) default false
30556  * @cfg {String} url action url
30557  * @cfg {String} paramName default 'imageUpload'
30558  * @cfg {String} method default POST
30559  * @cfg {Boolean} loadMask (true|false) default true
30560  * @cfg {Boolean} loadingText default 'Loading...'
30561  * 
30562  * @constructor
30563  * Create a new UploadCropbox
30564  * @param {Object} config The config object
30565  */
30566
30567 Roo.bootstrap.UploadCropbox = function(config){
30568     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30569     
30570     this.addEvents({
30571         /**
30572          * @event beforeselectfile
30573          * Fire before select file
30574          * @param {Roo.bootstrap.UploadCropbox} this
30575          */
30576         "beforeselectfile" : true,
30577         /**
30578          * @event initial
30579          * Fire after initEvent
30580          * @param {Roo.bootstrap.UploadCropbox} this
30581          */
30582         "initial" : true,
30583         /**
30584          * @event crop
30585          * Fire after initEvent
30586          * @param {Roo.bootstrap.UploadCropbox} this
30587          * @param {String} data
30588          */
30589         "crop" : true,
30590         /**
30591          * @event prepare
30592          * Fire when preparing the file data
30593          * @param {Roo.bootstrap.UploadCropbox} this
30594          * @param {Object} file
30595          */
30596         "prepare" : true,
30597         /**
30598          * @event exception
30599          * Fire when get exception
30600          * @param {Roo.bootstrap.UploadCropbox} this
30601          * @param {XMLHttpRequest} xhr
30602          */
30603         "exception" : true,
30604         /**
30605          * @event beforeloadcanvas
30606          * Fire before load the canvas
30607          * @param {Roo.bootstrap.UploadCropbox} this
30608          * @param {String} src
30609          */
30610         "beforeloadcanvas" : true,
30611         /**
30612          * @event trash
30613          * Fire when trash image
30614          * @param {Roo.bootstrap.UploadCropbox} this
30615          */
30616         "trash" : true,
30617         /**
30618          * @event download
30619          * Fire when download the image
30620          * @param {Roo.bootstrap.UploadCropbox} this
30621          */
30622         "download" : true,
30623         /**
30624          * @event footerbuttonclick
30625          * Fire when footerbuttonclick
30626          * @param {Roo.bootstrap.UploadCropbox} this
30627          * @param {String} type
30628          */
30629         "footerbuttonclick" : true,
30630         /**
30631          * @event resize
30632          * Fire when resize
30633          * @param {Roo.bootstrap.UploadCropbox} this
30634          */
30635         "resize" : true,
30636         /**
30637          * @event rotate
30638          * Fire when rotate the image
30639          * @param {Roo.bootstrap.UploadCropbox} this
30640          * @param {String} pos
30641          */
30642         "rotate" : true,
30643         /**
30644          * @event inspect
30645          * Fire when inspect the file
30646          * @param {Roo.bootstrap.UploadCropbox} this
30647          * @param {Object} file
30648          */
30649         "inspect" : true,
30650         /**
30651          * @event upload
30652          * Fire when xhr upload the file
30653          * @param {Roo.bootstrap.UploadCropbox} this
30654          * @param {Object} data
30655          */
30656         "upload" : true,
30657         /**
30658          * @event arrange
30659          * Fire when arrange the file data
30660          * @param {Roo.bootstrap.UploadCropbox} this
30661          * @param {Object} formData
30662          */
30663         "arrange" : true
30664     });
30665     
30666     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30667 };
30668
30669 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30670     
30671     emptyText : 'Click to upload image',
30672     rotateNotify : 'Image is too small to rotate',
30673     errorTimeout : 3000,
30674     scale : 0,
30675     baseScale : 1,
30676     rotate : 0,
30677     dragable : false,
30678     pinching : false,
30679     mouseX : 0,
30680     mouseY : 0,
30681     cropData : false,
30682     minWidth : 300,
30683     minHeight : 300,
30684     file : false,
30685     exif : {},
30686     baseRotate : 1,
30687     cropType : 'image/jpeg',
30688     buttons : false,
30689     canvasLoaded : false,
30690     isDocument : false,
30691     method : 'POST',
30692     paramName : 'imageUpload',
30693     loadMask : true,
30694     loadingText : 'Loading...',
30695     maskEl : false,
30696     
30697     getAutoCreate : function()
30698     {
30699         var cfg = {
30700             tag : 'div',
30701             cls : 'roo-upload-cropbox',
30702             cn : [
30703                 {
30704                     tag : 'input',
30705                     cls : 'roo-upload-cropbox-selector',
30706                     type : 'file'
30707                 },
30708                 {
30709                     tag : 'div',
30710                     cls : 'roo-upload-cropbox-body',
30711                     style : 'cursor:pointer',
30712                     cn : [
30713                         {
30714                             tag : 'div',
30715                             cls : 'roo-upload-cropbox-preview'
30716                         },
30717                         {
30718                             tag : 'div',
30719                             cls : 'roo-upload-cropbox-thumb'
30720                         },
30721                         {
30722                             tag : 'div',
30723                             cls : 'roo-upload-cropbox-empty-notify',
30724                             html : this.emptyText
30725                         },
30726                         {
30727                             tag : 'div',
30728                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30729                             html : this.rotateNotify
30730                         }
30731                     ]
30732                 },
30733                 {
30734                     tag : 'div',
30735                     cls : 'roo-upload-cropbox-footer',
30736                     cn : {
30737                         tag : 'div',
30738                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30739                         cn : []
30740                     }
30741                 }
30742             ]
30743         };
30744         
30745         return cfg;
30746     },
30747     
30748     onRender : function(ct, position)
30749     {
30750         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30751         
30752         if (this.buttons.length) {
30753             
30754             Roo.each(this.buttons, function(bb) {
30755                 
30756                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30757                 
30758                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30759                 
30760             }, this);
30761         }
30762         
30763         if(this.loadMask){
30764             this.maskEl = this.el;
30765         }
30766     },
30767     
30768     initEvents : function()
30769     {
30770         this.urlAPI = (window.createObjectURL && window) || 
30771                                 (window.URL && URL.revokeObjectURL && URL) || 
30772                                 (window.webkitURL && webkitURL);
30773                         
30774         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30775         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30776         
30777         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30778         this.selectorEl.hide();
30779         
30780         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30781         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30782         
30783         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30784         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30785         this.thumbEl.hide();
30786         
30787         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30788         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30789         
30790         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30791         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30792         this.errorEl.hide();
30793         
30794         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30795         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30796         this.footerEl.hide();
30797         
30798         this.setThumbBoxSize();
30799         
30800         this.bind();
30801         
30802         this.resize();
30803         
30804         this.fireEvent('initial', this);
30805     },
30806
30807     bind : function()
30808     {
30809         var _this = this;
30810         
30811         window.addEventListener("resize", function() { _this.resize(); } );
30812         
30813         this.bodyEl.on('click', this.beforeSelectFile, this);
30814         
30815         if(Roo.isTouch){
30816             this.bodyEl.on('touchstart', this.onTouchStart, this);
30817             this.bodyEl.on('touchmove', this.onTouchMove, this);
30818             this.bodyEl.on('touchend', this.onTouchEnd, this);
30819         }
30820         
30821         if(!Roo.isTouch){
30822             this.bodyEl.on('mousedown', this.onMouseDown, this);
30823             this.bodyEl.on('mousemove', this.onMouseMove, this);
30824             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30825             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30826             Roo.get(document).on('mouseup', this.onMouseUp, this);
30827         }
30828         
30829         this.selectorEl.on('change', this.onFileSelected, this);
30830     },
30831     
30832     reset : function()
30833     {    
30834         this.scale = 0;
30835         this.baseScale = 1;
30836         this.rotate = 0;
30837         this.baseRotate = 1;
30838         this.dragable = false;
30839         this.pinching = false;
30840         this.mouseX = 0;
30841         this.mouseY = 0;
30842         this.cropData = false;
30843         this.notifyEl.dom.innerHTML = this.emptyText;
30844         
30845         this.selectorEl.dom.value = '';
30846         
30847     },
30848     
30849     resize : function()
30850     {
30851         if(this.fireEvent('resize', this) != false){
30852             this.setThumbBoxPosition();
30853             this.setCanvasPosition();
30854         }
30855     },
30856     
30857     onFooterButtonClick : function(e, el, o, type)
30858     {
30859         switch (type) {
30860             case 'rotate-left' :
30861                 this.onRotateLeft(e);
30862                 break;
30863             case 'rotate-right' :
30864                 this.onRotateRight(e);
30865                 break;
30866             case 'picture' :
30867                 this.beforeSelectFile(e);
30868                 break;
30869             case 'trash' :
30870                 this.trash(e);
30871                 break;
30872             case 'crop' :
30873                 this.crop(e);
30874                 break;
30875             case 'download' :
30876                 this.download(e);
30877                 break;
30878             default :
30879                 break;
30880         }
30881         
30882         this.fireEvent('footerbuttonclick', this, type);
30883     },
30884     
30885     beforeSelectFile : function(e)
30886     {
30887         e.preventDefault();
30888         
30889         if(this.fireEvent('beforeselectfile', this) != false){
30890             this.selectorEl.dom.click();
30891         }
30892     },
30893     
30894     onFileSelected : function(e)
30895     {
30896         e.preventDefault();
30897         
30898         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30899             return;
30900         }
30901         
30902         var file = this.selectorEl.dom.files[0];
30903         
30904         if(this.fireEvent('inspect', this, file) != false){
30905             this.prepare(file);
30906         }
30907         
30908     },
30909     
30910     trash : function(e)
30911     {
30912         this.fireEvent('trash', this);
30913     },
30914     
30915     download : function(e)
30916     {
30917         this.fireEvent('download', this);
30918     },
30919     
30920     loadCanvas : function(src)
30921     {   
30922         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30923             
30924             this.reset();
30925             
30926             this.imageEl = document.createElement('img');
30927             
30928             var _this = this;
30929             
30930             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30931             
30932             this.imageEl.src = src;
30933         }
30934     },
30935     
30936     onLoadCanvas : function()
30937     {   
30938         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30939         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30940         
30941         this.bodyEl.un('click', this.beforeSelectFile, this);
30942         
30943         this.notifyEl.hide();
30944         this.thumbEl.show();
30945         this.footerEl.show();
30946         
30947         this.baseRotateLevel();
30948         
30949         if(this.isDocument){
30950             this.setThumbBoxSize();
30951         }
30952         
30953         this.setThumbBoxPosition();
30954         
30955         this.baseScaleLevel();
30956         
30957         this.draw();
30958         
30959         this.resize();
30960         
30961         this.canvasLoaded = true;
30962         
30963         if(this.loadMask){
30964             this.maskEl.unmask();
30965         }
30966         
30967     },
30968     
30969     setCanvasPosition : function()
30970     {   
30971         if(!this.canvasEl){
30972             return;
30973         }
30974         
30975         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30976         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30977         
30978         this.previewEl.setLeft(pw);
30979         this.previewEl.setTop(ph);
30980         
30981     },
30982     
30983     onMouseDown : function(e)
30984     {   
30985         e.stopEvent();
30986         
30987         this.dragable = true;
30988         this.pinching = false;
30989         
30990         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30991             this.dragable = false;
30992             return;
30993         }
30994         
30995         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30996         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30997         
30998     },
30999     
31000     onMouseMove : function(e)
31001     {   
31002         e.stopEvent();
31003         
31004         if(!this.canvasLoaded){
31005             return;
31006         }
31007         
31008         if (!this.dragable){
31009             return;
31010         }
31011         
31012         var minX = Math.ceil(this.thumbEl.getLeft(true));
31013         var minY = Math.ceil(this.thumbEl.getTop(true));
31014         
31015         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31016         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31017         
31018         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31019         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31020         
31021         x = x - this.mouseX;
31022         y = y - this.mouseY;
31023         
31024         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31025         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31026         
31027         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31028         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31029         
31030         this.previewEl.setLeft(bgX);
31031         this.previewEl.setTop(bgY);
31032         
31033         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31034         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31035     },
31036     
31037     onMouseUp : function(e)
31038     {   
31039         e.stopEvent();
31040         
31041         this.dragable = false;
31042     },
31043     
31044     onMouseWheel : function(e)
31045     {   
31046         e.stopEvent();
31047         
31048         this.startScale = this.scale;
31049         
31050         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31051         
31052         if(!this.zoomable()){
31053             this.scale = this.startScale;
31054             return;
31055         }
31056         
31057         this.draw();
31058         
31059         return;
31060     },
31061     
31062     zoomable : function()
31063     {
31064         var minScale = this.thumbEl.getWidth() / this.minWidth;
31065         
31066         if(this.minWidth < this.minHeight){
31067             minScale = this.thumbEl.getHeight() / this.minHeight;
31068         }
31069         
31070         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31071         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31072         
31073         if(
31074                 this.isDocument &&
31075                 (this.rotate == 0 || this.rotate == 180) && 
31076                 (
31077                     width > this.imageEl.OriginWidth || 
31078                     height > this.imageEl.OriginHeight ||
31079                     (width < this.minWidth && height < this.minHeight)
31080                 )
31081         ){
31082             return false;
31083         }
31084         
31085         if(
31086                 this.isDocument &&
31087                 (this.rotate == 90 || this.rotate == 270) && 
31088                 (
31089                     width > this.imageEl.OriginWidth || 
31090                     height > this.imageEl.OriginHeight ||
31091                     (width < this.minHeight && height < this.minWidth)
31092                 )
31093         ){
31094             return false;
31095         }
31096         
31097         if(
31098                 !this.isDocument &&
31099                 (this.rotate == 0 || this.rotate == 180) && 
31100                 (
31101                     width < this.minWidth || 
31102                     width > this.imageEl.OriginWidth || 
31103                     height < this.minHeight || 
31104                     height > this.imageEl.OriginHeight
31105                 )
31106         ){
31107             return false;
31108         }
31109         
31110         if(
31111                 !this.isDocument &&
31112                 (this.rotate == 90 || this.rotate == 270) && 
31113                 (
31114                     width < this.minHeight || 
31115                     width > this.imageEl.OriginWidth || 
31116                     height < this.minWidth || 
31117                     height > this.imageEl.OriginHeight
31118                 )
31119         ){
31120             return false;
31121         }
31122         
31123         return true;
31124         
31125     },
31126     
31127     onRotateLeft : function(e)
31128     {   
31129         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31130             
31131             var minScale = this.thumbEl.getWidth() / this.minWidth;
31132             
31133             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31134             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31135             
31136             this.startScale = this.scale;
31137             
31138             while (this.getScaleLevel() < minScale){
31139             
31140                 this.scale = this.scale + 1;
31141                 
31142                 if(!this.zoomable()){
31143                     break;
31144                 }
31145                 
31146                 if(
31147                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31148                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31149                 ){
31150                     continue;
31151                 }
31152                 
31153                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31154
31155                 this.draw();
31156                 
31157                 return;
31158             }
31159             
31160             this.scale = this.startScale;
31161             
31162             this.onRotateFail();
31163             
31164             return false;
31165         }
31166         
31167         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31168
31169         if(this.isDocument){
31170             this.setThumbBoxSize();
31171             this.setThumbBoxPosition();
31172             this.setCanvasPosition();
31173         }
31174         
31175         this.draw();
31176         
31177         this.fireEvent('rotate', this, 'left');
31178         
31179     },
31180     
31181     onRotateRight : function(e)
31182     {
31183         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31184             
31185             var minScale = this.thumbEl.getWidth() / this.minWidth;
31186         
31187             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31188             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31189             
31190             this.startScale = this.scale;
31191             
31192             while (this.getScaleLevel() < minScale){
31193             
31194                 this.scale = this.scale + 1;
31195                 
31196                 if(!this.zoomable()){
31197                     break;
31198                 }
31199                 
31200                 if(
31201                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31202                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31203                 ){
31204                     continue;
31205                 }
31206                 
31207                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31208
31209                 this.draw();
31210                 
31211                 return;
31212             }
31213             
31214             this.scale = this.startScale;
31215             
31216             this.onRotateFail();
31217             
31218             return false;
31219         }
31220         
31221         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31222
31223         if(this.isDocument){
31224             this.setThumbBoxSize();
31225             this.setThumbBoxPosition();
31226             this.setCanvasPosition();
31227         }
31228         
31229         this.draw();
31230         
31231         this.fireEvent('rotate', this, 'right');
31232     },
31233     
31234     onRotateFail : function()
31235     {
31236         this.errorEl.show(true);
31237         
31238         var _this = this;
31239         
31240         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31241     },
31242     
31243     draw : function()
31244     {
31245         this.previewEl.dom.innerHTML = '';
31246         
31247         var canvasEl = document.createElement("canvas");
31248         
31249         var contextEl = canvasEl.getContext("2d");
31250         
31251         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31252         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31253         var center = this.imageEl.OriginWidth / 2;
31254         
31255         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31256             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31257             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31258             center = this.imageEl.OriginHeight / 2;
31259         }
31260         
31261         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31262         
31263         contextEl.translate(center, center);
31264         contextEl.rotate(this.rotate * Math.PI / 180);
31265
31266         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31267         
31268         this.canvasEl = document.createElement("canvas");
31269         
31270         this.contextEl = this.canvasEl.getContext("2d");
31271         
31272         switch (this.rotate) {
31273             case 0 :
31274                 
31275                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31276                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31277                 
31278                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31279                 
31280                 break;
31281             case 90 : 
31282                 
31283                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31284                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31285                 
31286                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31287                     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);
31288                     break;
31289                 }
31290                 
31291                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31292                 
31293                 break;
31294             case 180 :
31295                 
31296                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31297                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31298                 
31299                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31300                     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);
31301                     break;
31302                 }
31303                 
31304                 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);
31305                 
31306                 break;
31307             case 270 :
31308                 
31309                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31310                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31311         
31312                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31313                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31314                     break;
31315                 }
31316                 
31317                 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);
31318                 
31319                 break;
31320             default : 
31321                 break;
31322         }
31323         
31324         this.previewEl.appendChild(this.canvasEl);
31325         
31326         this.setCanvasPosition();
31327     },
31328     
31329     crop : function()
31330     {
31331         if(!this.canvasLoaded){
31332             return;
31333         }
31334         
31335         var imageCanvas = document.createElement("canvas");
31336         
31337         var imageContext = imageCanvas.getContext("2d");
31338         
31339         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31340         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31341         
31342         var center = imageCanvas.width / 2;
31343         
31344         imageContext.translate(center, center);
31345         
31346         imageContext.rotate(this.rotate * Math.PI / 180);
31347         
31348         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31349         
31350         var canvas = document.createElement("canvas");
31351         
31352         var context = canvas.getContext("2d");
31353                 
31354         canvas.width = this.minWidth;
31355         canvas.height = this.minHeight;
31356
31357         switch (this.rotate) {
31358             case 0 :
31359                 
31360                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31361                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31362                 
31363                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31364                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31365                 
31366                 var targetWidth = this.minWidth - 2 * x;
31367                 var targetHeight = this.minHeight - 2 * y;
31368                 
31369                 var scale = 1;
31370                 
31371                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31372                     scale = targetWidth / width;
31373                 }
31374                 
31375                 if(x > 0 && y == 0){
31376                     scale = targetHeight / height;
31377                 }
31378                 
31379                 if(x > 0 && y > 0){
31380                     scale = targetWidth / width;
31381                     
31382                     if(width < height){
31383                         scale = targetHeight / height;
31384                     }
31385                 }
31386                 
31387                 context.scale(scale, scale);
31388                 
31389                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31390                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31391
31392                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31393                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31394
31395                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31396                 
31397                 break;
31398             case 90 : 
31399                 
31400                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31401                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31402                 
31403                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31404                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31405                 
31406                 var targetWidth = this.minWidth - 2 * x;
31407                 var targetHeight = this.minHeight - 2 * y;
31408                 
31409                 var scale = 1;
31410                 
31411                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31412                     scale = targetWidth / width;
31413                 }
31414                 
31415                 if(x > 0 && y == 0){
31416                     scale = targetHeight / height;
31417                 }
31418                 
31419                 if(x > 0 && y > 0){
31420                     scale = targetWidth / width;
31421                     
31422                     if(width < height){
31423                         scale = targetHeight / height;
31424                     }
31425                 }
31426                 
31427                 context.scale(scale, scale);
31428                 
31429                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31430                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31431
31432                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31433                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31434                 
31435                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31436                 
31437                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31438                 
31439                 break;
31440             case 180 :
31441                 
31442                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31443                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31444                 
31445                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31446                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31447                 
31448                 var targetWidth = this.minWidth - 2 * x;
31449                 var targetHeight = this.minHeight - 2 * y;
31450                 
31451                 var scale = 1;
31452                 
31453                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31454                     scale = targetWidth / width;
31455                 }
31456                 
31457                 if(x > 0 && y == 0){
31458                     scale = targetHeight / height;
31459                 }
31460                 
31461                 if(x > 0 && y > 0){
31462                     scale = targetWidth / width;
31463                     
31464                     if(width < height){
31465                         scale = targetHeight / height;
31466                     }
31467                 }
31468                 
31469                 context.scale(scale, scale);
31470                 
31471                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31472                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31473
31474                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31475                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31476
31477                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31478                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31479                 
31480                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31481                 
31482                 break;
31483             case 270 :
31484                 
31485                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31486                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31487                 
31488                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31489                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31490                 
31491                 var targetWidth = this.minWidth - 2 * x;
31492                 var targetHeight = this.minHeight - 2 * y;
31493                 
31494                 var scale = 1;
31495                 
31496                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31497                     scale = targetWidth / width;
31498                 }
31499                 
31500                 if(x > 0 && y == 0){
31501                     scale = targetHeight / height;
31502                 }
31503                 
31504                 if(x > 0 && y > 0){
31505                     scale = targetWidth / width;
31506                     
31507                     if(width < height){
31508                         scale = targetHeight / height;
31509                     }
31510                 }
31511                 
31512                 context.scale(scale, scale);
31513                 
31514                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31515                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31516
31517                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31518                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31519                 
31520                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31521                 
31522                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31523                 
31524                 break;
31525             default : 
31526                 break;
31527         }
31528         
31529         this.cropData = canvas.toDataURL(this.cropType);
31530         
31531         if(this.fireEvent('crop', this, this.cropData) !== false){
31532             this.process(this.file, this.cropData);
31533         }
31534         
31535         return;
31536         
31537     },
31538     
31539     setThumbBoxSize : function()
31540     {
31541         var width, height;
31542         
31543         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31544             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31545             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31546             
31547             this.minWidth = width;
31548             this.minHeight = height;
31549             
31550             if(this.rotate == 90 || this.rotate == 270){
31551                 this.minWidth = height;
31552                 this.minHeight = width;
31553             }
31554         }
31555         
31556         height = 300;
31557         width = Math.ceil(this.minWidth * height / this.minHeight);
31558         
31559         if(this.minWidth > this.minHeight){
31560             width = 300;
31561             height = Math.ceil(this.minHeight * width / this.minWidth);
31562         }
31563         
31564         this.thumbEl.setStyle({
31565             width : width + 'px',
31566             height : height + 'px'
31567         });
31568
31569         return;
31570             
31571     },
31572     
31573     setThumbBoxPosition : function()
31574     {
31575         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31576         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31577         
31578         this.thumbEl.setLeft(x);
31579         this.thumbEl.setTop(y);
31580         
31581     },
31582     
31583     baseRotateLevel : function()
31584     {
31585         this.baseRotate = 1;
31586         
31587         if(
31588                 typeof(this.exif) != 'undefined' &&
31589                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31590                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31591         ){
31592             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31593         }
31594         
31595         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31596         
31597     },
31598     
31599     baseScaleLevel : function()
31600     {
31601         var width, height;
31602         
31603         if(this.isDocument){
31604             
31605             if(this.baseRotate == 6 || this.baseRotate == 8){
31606             
31607                 height = this.thumbEl.getHeight();
31608                 this.baseScale = height / this.imageEl.OriginWidth;
31609
31610                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31611                     width = this.thumbEl.getWidth();
31612                     this.baseScale = width / this.imageEl.OriginHeight;
31613                 }
31614
31615                 return;
31616             }
31617
31618             height = this.thumbEl.getHeight();
31619             this.baseScale = height / this.imageEl.OriginHeight;
31620
31621             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31622                 width = this.thumbEl.getWidth();
31623                 this.baseScale = width / this.imageEl.OriginWidth;
31624             }
31625
31626             return;
31627         }
31628         
31629         if(this.baseRotate == 6 || this.baseRotate == 8){
31630             
31631             width = this.thumbEl.getHeight();
31632             this.baseScale = width / this.imageEl.OriginHeight;
31633             
31634             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31635                 height = this.thumbEl.getWidth();
31636                 this.baseScale = height / this.imageEl.OriginHeight;
31637             }
31638             
31639             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31640                 height = this.thumbEl.getWidth();
31641                 this.baseScale = height / this.imageEl.OriginHeight;
31642                 
31643                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31644                     width = this.thumbEl.getHeight();
31645                     this.baseScale = width / this.imageEl.OriginWidth;
31646                 }
31647             }
31648             
31649             return;
31650         }
31651         
31652         width = this.thumbEl.getWidth();
31653         this.baseScale = width / this.imageEl.OriginWidth;
31654         
31655         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31656             height = this.thumbEl.getHeight();
31657             this.baseScale = height / this.imageEl.OriginHeight;
31658         }
31659         
31660         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31661             
31662             height = this.thumbEl.getHeight();
31663             this.baseScale = height / this.imageEl.OriginHeight;
31664             
31665             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31666                 width = this.thumbEl.getWidth();
31667                 this.baseScale = width / this.imageEl.OriginWidth;
31668             }
31669             
31670         }
31671         
31672         return;
31673     },
31674     
31675     getScaleLevel : function()
31676     {
31677         return this.baseScale * Math.pow(1.1, this.scale);
31678     },
31679     
31680     onTouchStart : function(e)
31681     {
31682         if(!this.canvasLoaded){
31683             this.beforeSelectFile(e);
31684             return;
31685         }
31686         
31687         var touches = e.browserEvent.touches;
31688         
31689         if(!touches){
31690             return;
31691         }
31692         
31693         if(touches.length == 1){
31694             this.onMouseDown(e);
31695             return;
31696         }
31697         
31698         if(touches.length != 2){
31699             return;
31700         }
31701         
31702         var coords = [];
31703         
31704         for(var i = 0, finger; finger = touches[i]; i++){
31705             coords.push(finger.pageX, finger.pageY);
31706         }
31707         
31708         var x = Math.pow(coords[0] - coords[2], 2);
31709         var y = Math.pow(coords[1] - coords[3], 2);
31710         
31711         this.startDistance = Math.sqrt(x + y);
31712         
31713         this.startScale = this.scale;
31714         
31715         this.pinching = true;
31716         this.dragable = false;
31717         
31718     },
31719     
31720     onTouchMove : function(e)
31721     {
31722         if(!this.pinching && !this.dragable){
31723             return;
31724         }
31725         
31726         var touches = e.browserEvent.touches;
31727         
31728         if(!touches){
31729             return;
31730         }
31731         
31732         if(this.dragable){
31733             this.onMouseMove(e);
31734             return;
31735         }
31736         
31737         var coords = [];
31738         
31739         for(var i = 0, finger; finger = touches[i]; i++){
31740             coords.push(finger.pageX, finger.pageY);
31741         }
31742         
31743         var x = Math.pow(coords[0] - coords[2], 2);
31744         var y = Math.pow(coords[1] - coords[3], 2);
31745         
31746         this.endDistance = Math.sqrt(x + y);
31747         
31748         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31749         
31750         if(!this.zoomable()){
31751             this.scale = this.startScale;
31752             return;
31753         }
31754         
31755         this.draw();
31756         
31757     },
31758     
31759     onTouchEnd : function(e)
31760     {
31761         this.pinching = false;
31762         this.dragable = false;
31763         
31764     },
31765     
31766     process : function(file, crop)
31767     {
31768         if(this.loadMask){
31769             this.maskEl.mask(this.loadingText);
31770         }
31771         
31772         this.xhr = new XMLHttpRequest();
31773         
31774         file.xhr = this.xhr;
31775
31776         this.xhr.open(this.method, this.url, true);
31777         
31778         var headers = {
31779             "Accept": "application/json",
31780             "Cache-Control": "no-cache",
31781             "X-Requested-With": "XMLHttpRequest"
31782         };
31783         
31784         for (var headerName in headers) {
31785             var headerValue = headers[headerName];
31786             if (headerValue) {
31787                 this.xhr.setRequestHeader(headerName, headerValue);
31788             }
31789         }
31790         
31791         var _this = this;
31792         
31793         this.xhr.onload = function()
31794         {
31795             _this.xhrOnLoad(_this.xhr);
31796         }
31797         
31798         this.xhr.onerror = function()
31799         {
31800             _this.xhrOnError(_this.xhr);
31801         }
31802         
31803         var formData = new FormData();
31804
31805         formData.append('returnHTML', 'NO');
31806         
31807         if(crop){
31808             formData.append('crop', crop);
31809         }
31810         
31811         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31812             formData.append(this.paramName, file, file.name);
31813         }
31814         
31815         if(typeof(file.filename) != 'undefined'){
31816             formData.append('filename', file.filename);
31817         }
31818         
31819         if(typeof(file.mimetype) != 'undefined'){
31820             formData.append('mimetype', file.mimetype);
31821         }
31822         
31823         if(this.fireEvent('arrange', this, formData) != false){
31824             this.xhr.send(formData);
31825         };
31826     },
31827     
31828     xhrOnLoad : function(xhr)
31829     {
31830         if(this.loadMask){
31831             this.maskEl.unmask();
31832         }
31833         
31834         if (xhr.readyState !== 4) {
31835             this.fireEvent('exception', this, xhr);
31836             return;
31837         }
31838
31839         var response = Roo.decode(xhr.responseText);
31840         
31841         if(!response.success){
31842             this.fireEvent('exception', this, xhr);
31843             return;
31844         }
31845         
31846         var response = Roo.decode(xhr.responseText);
31847         
31848         this.fireEvent('upload', this, response);
31849         
31850     },
31851     
31852     xhrOnError : function()
31853     {
31854         if(this.loadMask){
31855             this.maskEl.unmask();
31856         }
31857         
31858         Roo.log('xhr on error');
31859         
31860         var response = Roo.decode(xhr.responseText);
31861           
31862         Roo.log(response);
31863         
31864     },
31865     
31866     prepare : function(file)
31867     {   
31868         if(this.loadMask){
31869             this.maskEl.mask(this.loadingText);
31870         }
31871         
31872         this.file = false;
31873         this.exif = {};
31874         
31875         if(typeof(file) === 'string'){
31876             this.loadCanvas(file);
31877             return;
31878         }
31879         
31880         if(!file || !this.urlAPI){
31881             return;
31882         }
31883         
31884         this.file = file;
31885         this.cropType = file.type;
31886         
31887         var _this = this;
31888         
31889         if(this.fireEvent('prepare', this, this.file) != false){
31890             
31891             var reader = new FileReader();
31892             
31893             reader.onload = function (e) {
31894                 if (e.target.error) {
31895                     Roo.log(e.target.error);
31896                     return;
31897                 }
31898                 
31899                 var buffer = e.target.result,
31900                     dataView = new DataView(buffer),
31901                     offset = 2,
31902                     maxOffset = dataView.byteLength - 4,
31903                     markerBytes,
31904                     markerLength;
31905                 
31906                 if (dataView.getUint16(0) === 0xffd8) {
31907                     while (offset < maxOffset) {
31908                         markerBytes = dataView.getUint16(offset);
31909                         
31910                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31911                             markerLength = dataView.getUint16(offset + 2) + 2;
31912                             if (offset + markerLength > dataView.byteLength) {
31913                                 Roo.log('Invalid meta data: Invalid segment size.');
31914                                 break;
31915                             }
31916                             
31917                             if(markerBytes == 0xffe1){
31918                                 _this.parseExifData(
31919                                     dataView,
31920                                     offset,
31921                                     markerLength
31922                                 );
31923                             }
31924                             
31925                             offset += markerLength;
31926                             
31927                             continue;
31928                         }
31929                         
31930                         break;
31931                     }
31932                     
31933                 }
31934                 
31935                 var url = _this.urlAPI.createObjectURL(_this.file);
31936                 
31937                 _this.loadCanvas(url);
31938                 
31939                 return;
31940             }
31941             
31942             reader.readAsArrayBuffer(this.file);
31943             
31944         }
31945         
31946     },
31947     
31948     parseExifData : function(dataView, offset, length)
31949     {
31950         var tiffOffset = offset + 10,
31951             littleEndian,
31952             dirOffset;
31953     
31954         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31955             // No Exif data, might be XMP data instead
31956             return;
31957         }
31958         
31959         // Check for the ASCII code for "Exif" (0x45786966):
31960         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31961             // No Exif data, might be XMP data instead
31962             return;
31963         }
31964         if (tiffOffset + 8 > dataView.byteLength) {
31965             Roo.log('Invalid Exif data: Invalid segment size.');
31966             return;
31967         }
31968         // Check for the two null bytes:
31969         if (dataView.getUint16(offset + 8) !== 0x0000) {
31970             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31971             return;
31972         }
31973         // Check the byte alignment:
31974         switch (dataView.getUint16(tiffOffset)) {
31975         case 0x4949:
31976             littleEndian = true;
31977             break;
31978         case 0x4D4D:
31979             littleEndian = false;
31980             break;
31981         default:
31982             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31983             return;
31984         }
31985         // Check for the TIFF tag marker (0x002A):
31986         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31987             Roo.log('Invalid Exif data: Missing TIFF marker.');
31988             return;
31989         }
31990         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31991         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31992         
31993         this.parseExifTags(
31994             dataView,
31995             tiffOffset,
31996             tiffOffset + dirOffset,
31997             littleEndian
31998         );
31999     },
32000     
32001     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32002     {
32003         var tagsNumber,
32004             dirEndOffset,
32005             i;
32006         if (dirOffset + 6 > dataView.byteLength) {
32007             Roo.log('Invalid Exif data: Invalid directory offset.');
32008             return;
32009         }
32010         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32011         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32012         if (dirEndOffset + 4 > dataView.byteLength) {
32013             Roo.log('Invalid Exif data: Invalid directory size.');
32014             return;
32015         }
32016         for (i = 0; i < tagsNumber; i += 1) {
32017             this.parseExifTag(
32018                 dataView,
32019                 tiffOffset,
32020                 dirOffset + 2 + 12 * i, // tag offset
32021                 littleEndian
32022             );
32023         }
32024         // Return the offset to the next directory:
32025         return dataView.getUint32(dirEndOffset, littleEndian);
32026     },
32027     
32028     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32029     {
32030         var tag = dataView.getUint16(offset, littleEndian);
32031         
32032         this.exif[tag] = this.getExifValue(
32033             dataView,
32034             tiffOffset,
32035             offset,
32036             dataView.getUint16(offset + 2, littleEndian), // tag type
32037             dataView.getUint32(offset + 4, littleEndian), // tag length
32038             littleEndian
32039         );
32040     },
32041     
32042     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32043     {
32044         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32045             tagSize,
32046             dataOffset,
32047             values,
32048             i,
32049             str,
32050             c;
32051     
32052         if (!tagType) {
32053             Roo.log('Invalid Exif data: Invalid tag type.');
32054             return;
32055         }
32056         
32057         tagSize = tagType.size * length;
32058         // Determine if the value is contained in the dataOffset bytes,
32059         // or if the value at the dataOffset is a pointer to the actual data:
32060         dataOffset = tagSize > 4 ?
32061                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32062         if (dataOffset + tagSize > dataView.byteLength) {
32063             Roo.log('Invalid Exif data: Invalid data offset.');
32064             return;
32065         }
32066         if (length === 1) {
32067             return tagType.getValue(dataView, dataOffset, littleEndian);
32068         }
32069         values = [];
32070         for (i = 0; i < length; i += 1) {
32071             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32072         }
32073         
32074         if (tagType.ascii) {
32075             str = '';
32076             // Concatenate the chars:
32077             for (i = 0; i < values.length; i += 1) {
32078                 c = values[i];
32079                 // Ignore the terminating NULL byte(s):
32080                 if (c === '\u0000') {
32081                     break;
32082                 }
32083                 str += c;
32084             }
32085             return str;
32086         }
32087         return values;
32088     }
32089     
32090 });
32091
32092 Roo.apply(Roo.bootstrap.UploadCropbox, {
32093     tags : {
32094         'Orientation': 0x0112
32095     },
32096     
32097     Orientation: {
32098             1: 0, //'top-left',
32099 //            2: 'top-right',
32100             3: 180, //'bottom-right',
32101 //            4: 'bottom-left',
32102 //            5: 'left-top',
32103             6: 90, //'right-top',
32104 //            7: 'right-bottom',
32105             8: 270 //'left-bottom'
32106     },
32107     
32108     exifTagTypes : {
32109         // byte, 8-bit unsigned int:
32110         1: {
32111             getValue: function (dataView, dataOffset) {
32112                 return dataView.getUint8(dataOffset);
32113             },
32114             size: 1
32115         },
32116         // ascii, 8-bit byte:
32117         2: {
32118             getValue: function (dataView, dataOffset) {
32119                 return String.fromCharCode(dataView.getUint8(dataOffset));
32120             },
32121             size: 1,
32122             ascii: true
32123         },
32124         // short, 16 bit int:
32125         3: {
32126             getValue: function (dataView, dataOffset, littleEndian) {
32127                 return dataView.getUint16(dataOffset, littleEndian);
32128             },
32129             size: 2
32130         },
32131         // long, 32 bit int:
32132         4: {
32133             getValue: function (dataView, dataOffset, littleEndian) {
32134                 return dataView.getUint32(dataOffset, littleEndian);
32135             },
32136             size: 4
32137         },
32138         // rational = two long values, first is numerator, second is denominator:
32139         5: {
32140             getValue: function (dataView, dataOffset, littleEndian) {
32141                 return dataView.getUint32(dataOffset, littleEndian) /
32142                     dataView.getUint32(dataOffset + 4, littleEndian);
32143             },
32144             size: 8
32145         },
32146         // slong, 32 bit signed int:
32147         9: {
32148             getValue: function (dataView, dataOffset, littleEndian) {
32149                 return dataView.getInt32(dataOffset, littleEndian);
32150             },
32151             size: 4
32152         },
32153         // srational, two slongs, first is numerator, second is denominator:
32154         10: {
32155             getValue: function (dataView, dataOffset, littleEndian) {
32156                 return dataView.getInt32(dataOffset, littleEndian) /
32157                     dataView.getInt32(dataOffset + 4, littleEndian);
32158             },
32159             size: 8
32160         }
32161     },
32162     
32163     footer : {
32164         STANDARD : [
32165             {
32166                 tag : 'div',
32167                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32168                 action : 'rotate-left',
32169                 cn : [
32170                     {
32171                         tag : 'button',
32172                         cls : 'btn btn-default',
32173                         html : '<i class="fa fa-undo"></i>'
32174                     }
32175                 ]
32176             },
32177             {
32178                 tag : 'div',
32179                 cls : 'btn-group roo-upload-cropbox-picture',
32180                 action : 'picture',
32181                 cn : [
32182                     {
32183                         tag : 'button',
32184                         cls : 'btn btn-default',
32185                         html : '<i class="fa fa-picture-o"></i>'
32186                     }
32187                 ]
32188             },
32189             {
32190                 tag : 'div',
32191                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32192                 action : 'rotate-right',
32193                 cn : [
32194                     {
32195                         tag : 'button',
32196                         cls : 'btn btn-default',
32197                         html : '<i class="fa fa-repeat"></i>'
32198                     }
32199                 ]
32200             }
32201         ],
32202         DOCUMENT : [
32203             {
32204                 tag : 'div',
32205                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32206                 action : 'rotate-left',
32207                 cn : [
32208                     {
32209                         tag : 'button',
32210                         cls : 'btn btn-default',
32211                         html : '<i class="fa fa-undo"></i>'
32212                     }
32213                 ]
32214             },
32215             {
32216                 tag : 'div',
32217                 cls : 'btn-group roo-upload-cropbox-download',
32218                 action : 'download',
32219                 cn : [
32220                     {
32221                         tag : 'button',
32222                         cls : 'btn btn-default',
32223                         html : '<i class="fa fa-download"></i>'
32224                     }
32225                 ]
32226             },
32227             {
32228                 tag : 'div',
32229                 cls : 'btn-group roo-upload-cropbox-crop',
32230                 action : 'crop',
32231                 cn : [
32232                     {
32233                         tag : 'button',
32234                         cls : 'btn btn-default',
32235                         html : '<i class="fa fa-crop"></i>'
32236                     }
32237                 ]
32238             },
32239             {
32240                 tag : 'div',
32241                 cls : 'btn-group roo-upload-cropbox-trash',
32242                 action : 'trash',
32243                 cn : [
32244                     {
32245                         tag : 'button',
32246                         cls : 'btn btn-default',
32247                         html : '<i class="fa fa-trash"></i>'
32248                     }
32249                 ]
32250             },
32251             {
32252                 tag : 'div',
32253                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32254                 action : 'rotate-right',
32255                 cn : [
32256                     {
32257                         tag : 'button',
32258                         cls : 'btn btn-default',
32259                         html : '<i class="fa fa-repeat"></i>'
32260                     }
32261                 ]
32262             }
32263         ],
32264         ROTATOR : [
32265             {
32266                 tag : 'div',
32267                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32268                 action : 'rotate-left',
32269                 cn : [
32270                     {
32271                         tag : 'button',
32272                         cls : 'btn btn-default',
32273                         html : '<i class="fa fa-undo"></i>'
32274                     }
32275                 ]
32276             },
32277             {
32278                 tag : 'div',
32279                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32280                 action : 'rotate-right',
32281                 cn : [
32282                     {
32283                         tag : 'button',
32284                         cls : 'btn btn-default',
32285                         html : '<i class="fa fa-repeat"></i>'
32286                     }
32287                 ]
32288             }
32289         ]
32290     }
32291 });
32292
32293 /*
32294 * Licence: LGPL
32295 */
32296
32297 /**
32298  * @class Roo.bootstrap.DocumentManager
32299  * @extends Roo.bootstrap.Component
32300  * Bootstrap DocumentManager class
32301  * @cfg {String} paramName default 'imageUpload'
32302  * @cfg {String} toolTipName default 'filename'
32303  * @cfg {String} method default POST
32304  * @cfg {String} url action url
32305  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32306  * @cfg {Boolean} multiple multiple upload default true
32307  * @cfg {Number} thumbSize default 300
32308  * @cfg {String} fieldLabel
32309  * @cfg {Number} labelWidth default 4
32310  * @cfg {String} labelAlign (left|top) default left
32311  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32312 * @cfg {Number} labellg set the width of label (1-12)
32313  * @cfg {Number} labelmd set the width of label (1-12)
32314  * @cfg {Number} labelsm set the width of label (1-12)
32315  * @cfg {Number} labelxs set the width of label (1-12)
32316  * 
32317  * @constructor
32318  * Create a new DocumentManager
32319  * @param {Object} config The config object
32320  */
32321
32322 Roo.bootstrap.DocumentManager = function(config){
32323     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32324     
32325     this.files = [];
32326     this.delegates = [];
32327     
32328     this.addEvents({
32329         /**
32330          * @event initial
32331          * Fire when initial the DocumentManager
32332          * @param {Roo.bootstrap.DocumentManager} this
32333          */
32334         "initial" : true,
32335         /**
32336          * @event inspect
32337          * inspect selected file
32338          * @param {Roo.bootstrap.DocumentManager} this
32339          * @param {File} file
32340          */
32341         "inspect" : true,
32342         /**
32343          * @event exception
32344          * Fire when xhr load exception
32345          * @param {Roo.bootstrap.DocumentManager} this
32346          * @param {XMLHttpRequest} xhr
32347          */
32348         "exception" : true,
32349         /**
32350          * @event afterupload
32351          * Fire when xhr load exception
32352          * @param {Roo.bootstrap.DocumentManager} this
32353          * @param {XMLHttpRequest} xhr
32354          */
32355         "afterupload" : true,
32356         /**
32357          * @event prepare
32358          * prepare the form data
32359          * @param {Roo.bootstrap.DocumentManager} this
32360          * @param {Object} formData
32361          */
32362         "prepare" : true,
32363         /**
32364          * @event remove
32365          * Fire when remove the file
32366          * @param {Roo.bootstrap.DocumentManager} this
32367          * @param {Object} file
32368          */
32369         "remove" : true,
32370         /**
32371          * @event refresh
32372          * Fire after refresh the file
32373          * @param {Roo.bootstrap.DocumentManager} this
32374          */
32375         "refresh" : true,
32376         /**
32377          * @event click
32378          * Fire after click the image
32379          * @param {Roo.bootstrap.DocumentManager} this
32380          * @param {Object} file
32381          */
32382         "click" : true,
32383         /**
32384          * @event edit
32385          * Fire when upload a image and editable set to true
32386          * @param {Roo.bootstrap.DocumentManager} this
32387          * @param {Object} file
32388          */
32389         "edit" : true,
32390         /**
32391          * @event beforeselectfile
32392          * Fire before select file
32393          * @param {Roo.bootstrap.DocumentManager} this
32394          */
32395         "beforeselectfile" : true,
32396         /**
32397          * @event process
32398          * Fire before process file
32399          * @param {Roo.bootstrap.DocumentManager} this
32400          * @param {Object} file
32401          */
32402         "process" : true,
32403         /**
32404          * @event previewrendered
32405          * Fire when preview rendered
32406          * @param {Roo.bootstrap.DocumentManager} this
32407          * @param {Object} file
32408          */
32409         "previewrendered" : true,
32410         /**
32411          */
32412         "previewResize" : true
32413         
32414     });
32415 };
32416
32417 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32418     
32419     boxes : 0,
32420     inputName : '',
32421     thumbSize : 300,
32422     multiple : true,
32423     files : false,
32424     method : 'POST',
32425     url : '',
32426     paramName : 'imageUpload',
32427     toolTipName : 'filename',
32428     fieldLabel : '',
32429     labelWidth : 4,
32430     labelAlign : 'left',
32431     editable : true,
32432     delegates : false,
32433     xhr : false, 
32434     
32435     labellg : 0,
32436     labelmd : 0,
32437     labelsm : 0,
32438     labelxs : 0,
32439     
32440     getAutoCreate : function()
32441     {   
32442         var managerWidget = {
32443             tag : 'div',
32444             cls : 'roo-document-manager',
32445             cn : [
32446                 {
32447                     tag : 'input',
32448                     cls : 'roo-document-manager-selector',
32449                     type : 'file'
32450                 },
32451                 {
32452                     tag : 'div',
32453                     cls : 'roo-document-manager-uploader',
32454                     cn : [
32455                         {
32456                             tag : 'div',
32457                             cls : 'roo-document-manager-upload-btn',
32458                             html : '<i class="fa fa-plus"></i>'
32459                         }
32460                     ]
32461                     
32462                 }
32463             ]
32464         };
32465         
32466         var content = [
32467             {
32468                 tag : 'div',
32469                 cls : 'column col-md-12',
32470                 cn : managerWidget
32471             }
32472         ];
32473         
32474         if(this.fieldLabel.length){
32475             
32476             content = [
32477                 {
32478                     tag : 'div',
32479                     cls : 'column col-md-12',
32480                     html : this.fieldLabel
32481                 },
32482                 {
32483                     tag : 'div',
32484                     cls : 'column col-md-12',
32485                     cn : managerWidget
32486                 }
32487             ];
32488
32489             if(this.labelAlign == 'left'){
32490                 content = [
32491                     {
32492                         tag : 'div',
32493                         cls : 'column',
32494                         html : this.fieldLabel
32495                     },
32496                     {
32497                         tag : 'div',
32498                         cls : 'column',
32499                         cn : managerWidget
32500                     }
32501                 ];
32502                 
32503                 if(this.labelWidth > 12){
32504                     content[0].style = "width: " + this.labelWidth + 'px';
32505                 }
32506
32507                 if(this.labelWidth < 13 && this.labelmd == 0){
32508                     this.labelmd = this.labelWidth;
32509                 }
32510
32511                 if(this.labellg > 0){
32512                     content[0].cls += ' col-lg-' + this.labellg;
32513                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32514                 }
32515
32516                 if(this.labelmd > 0){
32517                     content[0].cls += ' col-md-' + this.labelmd;
32518                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32519                 }
32520
32521                 if(this.labelsm > 0){
32522                     content[0].cls += ' col-sm-' + this.labelsm;
32523                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32524                 }
32525
32526                 if(this.labelxs > 0){
32527                     content[0].cls += ' col-xs-' + this.labelxs;
32528                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32529                 }
32530                 
32531             }
32532         }
32533         
32534         var cfg = {
32535             tag : 'div',
32536             cls : 'row clearfix',
32537             cn : content
32538         };
32539         
32540         return cfg;
32541         
32542     },
32543     
32544     initEvents : function()
32545     {
32546         this.managerEl = this.el.select('.roo-document-manager', true).first();
32547         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32548         
32549         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32550         this.selectorEl.hide();
32551         
32552         if(this.multiple){
32553             this.selectorEl.attr('multiple', 'multiple');
32554         }
32555         
32556         this.selectorEl.on('change', this.onFileSelected, this);
32557         
32558         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32559         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32560         
32561         this.uploader.on('click', this.onUploaderClick, this);
32562         
32563         this.renderProgressDialog();
32564         
32565         var _this = this;
32566         
32567         window.addEventListener("resize", function() { _this.refresh(); } );
32568         
32569         this.fireEvent('initial', this);
32570     },
32571     
32572     renderProgressDialog : function()
32573     {
32574         var _this = this;
32575         
32576         this.progressDialog = new Roo.bootstrap.Modal({
32577             cls : 'roo-document-manager-progress-dialog',
32578             allow_close : false,
32579             animate : false,
32580             title : '',
32581             buttons : [
32582                 {
32583                     name  :'cancel',
32584                     weight : 'danger',
32585                     html : 'Cancel'
32586                 }
32587             ], 
32588             listeners : { 
32589                 btnclick : function() {
32590                     _this.uploadCancel();
32591                     this.hide();
32592                 }
32593             }
32594         });
32595          
32596         this.progressDialog.render(Roo.get(document.body));
32597          
32598         this.progress = new Roo.bootstrap.Progress({
32599             cls : 'roo-document-manager-progress',
32600             active : true,
32601             striped : true
32602         });
32603         
32604         this.progress.render(this.progressDialog.getChildContainer());
32605         
32606         this.progressBar = new Roo.bootstrap.ProgressBar({
32607             cls : 'roo-document-manager-progress-bar',
32608             aria_valuenow : 0,
32609             aria_valuemin : 0,
32610             aria_valuemax : 12,
32611             panel : 'success'
32612         });
32613         
32614         this.progressBar.render(this.progress.getChildContainer());
32615     },
32616     
32617     onUploaderClick : function(e)
32618     {
32619         e.preventDefault();
32620      
32621         if(this.fireEvent('beforeselectfile', this) != false){
32622             this.selectorEl.dom.click();
32623         }
32624         
32625     },
32626     
32627     onFileSelected : function(e)
32628     {
32629         e.preventDefault();
32630         
32631         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32632             return;
32633         }
32634         
32635         Roo.each(this.selectorEl.dom.files, function(file){
32636             if(this.fireEvent('inspect', this, file) != false){
32637                 this.files.push(file);
32638             }
32639         }, this);
32640         
32641         this.queue();
32642         
32643     },
32644     
32645     queue : function()
32646     {
32647         this.selectorEl.dom.value = '';
32648         
32649         if(!this.files || !this.files.length){
32650             return;
32651         }
32652         
32653         if(this.boxes > 0 && this.files.length > this.boxes){
32654             this.files = this.files.slice(0, this.boxes);
32655         }
32656         
32657         this.uploader.show();
32658         
32659         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32660             this.uploader.hide();
32661         }
32662         
32663         var _this = this;
32664         
32665         var files = [];
32666         
32667         var docs = [];
32668         
32669         Roo.each(this.files, function(file){
32670             
32671             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32672                 var f = this.renderPreview(file);
32673                 files.push(f);
32674                 return;
32675             }
32676             
32677             if(file.type.indexOf('image') != -1){
32678                 this.delegates.push(
32679                     (function(){
32680                         _this.process(file);
32681                     }).createDelegate(this)
32682                 );
32683         
32684                 return;
32685             }
32686             
32687             docs.push(
32688                 (function(){
32689                     _this.process(file);
32690                 }).createDelegate(this)
32691             );
32692             
32693         }, this);
32694         
32695         this.files = files;
32696         
32697         this.delegates = this.delegates.concat(docs);
32698         
32699         if(!this.delegates.length){
32700             this.refresh();
32701             return;
32702         }
32703         
32704         this.progressBar.aria_valuemax = this.delegates.length;
32705         
32706         this.arrange();
32707         
32708         return;
32709     },
32710     
32711     arrange : function()
32712     {
32713         if(!this.delegates.length){
32714             this.progressDialog.hide();
32715             this.refresh();
32716             return;
32717         }
32718         
32719         var delegate = this.delegates.shift();
32720         
32721         this.progressDialog.show();
32722         
32723         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32724         
32725         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32726         
32727         delegate();
32728     },
32729     
32730     refresh : function()
32731     {
32732         this.uploader.show();
32733         
32734         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32735             this.uploader.hide();
32736         }
32737         
32738         Roo.isTouch ? this.closable(false) : this.closable(true);
32739         
32740         this.fireEvent('refresh', this);
32741     },
32742     
32743     onRemove : function(e, el, o)
32744     {
32745         e.preventDefault();
32746         
32747         this.fireEvent('remove', this, o);
32748         
32749     },
32750     
32751     remove : function(o)
32752     {
32753         var files = [];
32754         
32755         Roo.each(this.files, function(file){
32756             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32757                 files.push(file);
32758                 return;
32759             }
32760
32761             o.target.remove();
32762
32763         }, this);
32764         
32765         this.files = files;
32766         
32767         this.refresh();
32768     },
32769     
32770     clear : function()
32771     {
32772         Roo.each(this.files, function(file){
32773             if(!file.target){
32774                 return;
32775             }
32776             
32777             file.target.remove();
32778
32779         }, this);
32780         
32781         this.files = [];
32782         
32783         this.refresh();
32784     },
32785     
32786     onClick : function(e, el, o)
32787     {
32788         e.preventDefault();
32789         
32790         this.fireEvent('click', this, o);
32791         
32792     },
32793     
32794     closable : function(closable)
32795     {
32796         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32797             
32798             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32799             
32800             if(closable){
32801                 el.show();
32802                 return;
32803             }
32804             
32805             el.hide();
32806             
32807         }, this);
32808     },
32809     
32810     xhrOnLoad : function(xhr)
32811     {
32812         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32813             el.remove();
32814         }, this);
32815         
32816         if (xhr.readyState !== 4) {
32817             this.arrange();
32818             this.fireEvent('exception', this, xhr);
32819             return;
32820         }
32821
32822         var response = Roo.decode(xhr.responseText);
32823         
32824         if(!response.success){
32825             this.arrange();
32826             this.fireEvent('exception', this, xhr);
32827             return;
32828         }
32829         
32830         var file = this.renderPreview(response.data);
32831         
32832         this.files.push(file);
32833         
32834         this.arrange();
32835         
32836         this.fireEvent('afterupload', this, xhr);
32837         
32838     },
32839     
32840     xhrOnError : function(xhr)
32841     {
32842         Roo.log('xhr on error');
32843         
32844         var response = Roo.decode(xhr.responseText);
32845           
32846         Roo.log(response);
32847         
32848         this.arrange();
32849     },
32850     
32851     process : function(file)
32852     {
32853         if(this.fireEvent('process', this, file) !== false){
32854             if(this.editable && file.type.indexOf('image') != -1){
32855                 this.fireEvent('edit', this, file);
32856                 return;
32857             }
32858
32859             this.uploadStart(file, false);
32860
32861             return;
32862         }
32863         
32864     },
32865     
32866     uploadStart : function(file, crop)
32867     {
32868         this.xhr = new XMLHttpRequest();
32869         
32870         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32871             this.arrange();
32872             return;
32873         }
32874         
32875         file.xhr = this.xhr;
32876             
32877         this.managerEl.createChild({
32878             tag : 'div',
32879             cls : 'roo-document-manager-loading',
32880             cn : [
32881                 {
32882                     tag : 'div',
32883                     tooltip : file.name,
32884                     cls : 'roo-document-manager-thumb',
32885                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32886                 }
32887             ]
32888
32889         });
32890
32891         this.xhr.open(this.method, this.url, true);
32892         
32893         var headers = {
32894             "Accept": "application/json",
32895             "Cache-Control": "no-cache",
32896             "X-Requested-With": "XMLHttpRequest"
32897         };
32898         
32899         for (var headerName in headers) {
32900             var headerValue = headers[headerName];
32901             if (headerValue) {
32902                 this.xhr.setRequestHeader(headerName, headerValue);
32903             }
32904         }
32905         
32906         var _this = this;
32907         
32908         this.xhr.onload = function()
32909         {
32910             _this.xhrOnLoad(_this.xhr);
32911         }
32912         
32913         this.xhr.onerror = function()
32914         {
32915             _this.xhrOnError(_this.xhr);
32916         }
32917         
32918         var formData = new FormData();
32919
32920         formData.append('returnHTML', 'NO');
32921         
32922         if(crop){
32923             formData.append('crop', crop);
32924         }
32925         
32926         formData.append(this.paramName, file, file.name);
32927         
32928         var options = {
32929             file : file, 
32930             manually : false
32931         };
32932         
32933         if(this.fireEvent('prepare', this, formData, options) != false){
32934             
32935             if(options.manually){
32936                 return;
32937             }
32938             
32939             this.xhr.send(formData);
32940             return;
32941         };
32942         
32943         this.uploadCancel();
32944     },
32945     
32946     uploadCancel : function()
32947     {
32948         if (this.xhr) {
32949             this.xhr.abort();
32950         }
32951         
32952         this.delegates = [];
32953         
32954         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32955             el.remove();
32956         }, this);
32957         
32958         this.arrange();
32959     },
32960     
32961     renderPreview : function(file)
32962     {
32963         if(typeof(file.target) != 'undefined' && file.target){
32964             return file;
32965         }
32966         
32967         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32968         
32969         var previewEl = this.managerEl.createChild({
32970             tag : 'div',
32971             cls : 'roo-document-manager-preview',
32972             cn : [
32973                 {
32974                     tag : 'div',
32975                     tooltip : file[this.toolTipName],
32976                     cls : 'roo-document-manager-thumb',
32977                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32978                 },
32979                 {
32980                     tag : 'button',
32981                     cls : 'close',
32982                     html : '<i class="fa fa-times-circle"></i>'
32983                 }
32984             ]
32985         });
32986
32987         var close = previewEl.select('button.close', true).first();
32988
32989         close.on('click', this.onRemove, this, file);
32990
32991         file.target = previewEl;
32992
32993         var image = previewEl.select('img', true).first();
32994         
32995         var _this = this;
32996         
32997         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32998         
32999         image.on('click', this.onClick, this, file);
33000         
33001         this.fireEvent('previewrendered', this, file);
33002         
33003         return file;
33004         
33005     },
33006     
33007     onPreviewLoad : function(file, image)
33008     {
33009         if(typeof(file.target) == 'undefined' || !file.target){
33010             return;
33011         }
33012         
33013         var width = image.dom.naturalWidth || image.dom.width;
33014         var height = image.dom.naturalHeight || image.dom.height;
33015         
33016         if(!this.previewResize) {
33017             return;
33018         }
33019         
33020         if(width > height){
33021             file.target.addClass('wide');
33022             return;
33023         }
33024         
33025         file.target.addClass('tall');
33026         return;
33027         
33028     },
33029     
33030     uploadFromSource : function(file, crop)
33031     {
33032         this.xhr = new XMLHttpRequest();
33033         
33034         this.managerEl.createChild({
33035             tag : 'div',
33036             cls : 'roo-document-manager-loading',
33037             cn : [
33038                 {
33039                     tag : 'div',
33040                     tooltip : file.name,
33041                     cls : 'roo-document-manager-thumb',
33042                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33043                 }
33044             ]
33045
33046         });
33047
33048         this.xhr.open(this.method, this.url, true);
33049         
33050         var headers = {
33051             "Accept": "application/json",
33052             "Cache-Control": "no-cache",
33053             "X-Requested-With": "XMLHttpRequest"
33054         };
33055         
33056         for (var headerName in headers) {
33057             var headerValue = headers[headerName];
33058             if (headerValue) {
33059                 this.xhr.setRequestHeader(headerName, headerValue);
33060             }
33061         }
33062         
33063         var _this = this;
33064         
33065         this.xhr.onload = function()
33066         {
33067             _this.xhrOnLoad(_this.xhr);
33068         }
33069         
33070         this.xhr.onerror = function()
33071         {
33072             _this.xhrOnError(_this.xhr);
33073         }
33074         
33075         var formData = new FormData();
33076
33077         formData.append('returnHTML', 'NO');
33078         
33079         formData.append('crop', crop);
33080         
33081         if(typeof(file.filename) != 'undefined'){
33082             formData.append('filename', file.filename);
33083         }
33084         
33085         if(typeof(file.mimetype) != 'undefined'){
33086             formData.append('mimetype', file.mimetype);
33087         }
33088         
33089         Roo.log(formData);
33090         
33091         if(this.fireEvent('prepare', this, formData) != false){
33092             this.xhr.send(formData);
33093         };
33094     }
33095 });
33096
33097 /*
33098 * Licence: LGPL
33099 */
33100
33101 /**
33102  * @class Roo.bootstrap.DocumentViewer
33103  * @extends Roo.bootstrap.Component
33104  * Bootstrap DocumentViewer class
33105  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33106  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33107  * 
33108  * @constructor
33109  * Create a new DocumentViewer
33110  * @param {Object} config The config object
33111  */
33112
33113 Roo.bootstrap.DocumentViewer = function(config){
33114     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33115     
33116     this.addEvents({
33117         /**
33118          * @event initial
33119          * Fire after initEvent
33120          * @param {Roo.bootstrap.DocumentViewer} this
33121          */
33122         "initial" : true,
33123         /**
33124          * @event click
33125          * Fire after click
33126          * @param {Roo.bootstrap.DocumentViewer} this
33127          */
33128         "click" : true,
33129         /**
33130          * @event download
33131          * Fire after download button
33132          * @param {Roo.bootstrap.DocumentViewer} this
33133          */
33134         "download" : true,
33135         /**
33136          * @event trash
33137          * Fire after trash button
33138          * @param {Roo.bootstrap.DocumentViewer} this
33139          */
33140         "trash" : true
33141         
33142     });
33143 };
33144
33145 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33146     
33147     showDownload : true,
33148     
33149     showTrash : true,
33150     
33151     getAutoCreate : function()
33152     {
33153         var cfg = {
33154             tag : 'div',
33155             cls : 'roo-document-viewer',
33156             cn : [
33157                 {
33158                     tag : 'div',
33159                     cls : 'roo-document-viewer-body',
33160                     cn : [
33161                         {
33162                             tag : 'div',
33163                             cls : 'roo-document-viewer-thumb',
33164                             cn : [
33165                                 {
33166                                     tag : 'img',
33167                                     cls : 'roo-document-viewer-image'
33168                                 }
33169                             ]
33170                         }
33171                     ]
33172                 },
33173                 {
33174                     tag : 'div',
33175                     cls : 'roo-document-viewer-footer',
33176                     cn : {
33177                         tag : 'div',
33178                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33179                         cn : [
33180                             {
33181                                 tag : 'div',
33182                                 cls : 'btn-group roo-document-viewer-download',
33183                                 cn : [
33184                                     {
33185                                         tag : 'button',
33186                                         cls : 'btn btn-default',
33187                                         html : '<i class="fa fa-download"></i>'
33188                                     }
33189                                 ]
33190                             },
33191                             {
33192                                 tag : 'div',
33193                                 cls : 'btn-group roo-document-viewer-trash',
33194                                 cn : [
33195                                     {
33196                                         tag : 'button',
33197                                         cls : 'btn btn-default',
33198                                         html : '<i class="fa fa-trash"></i>'
33199                                     }
33200                                 ]
33201                             }
33202                         ]
33203                     }
33204                 }
33205             ]
33206         };
33207         
33208         return cfg;
33209     },
33210     
33211     initEvents : function()
33212     {
33213         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33214         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33215         
33216         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33217         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33218         
33219         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33220         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33221         
33222         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33223         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33224         
33225         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33226         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33227         
33228         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33229         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33230         
33231         this.bodyEl.on('click', this.onClick, this);
33232         this.downloadBtn.on('click', this.onDownload, this);
33233         this.trashBtn.on('click', this.onTrash, this);
33234         
33235         this.downloadBtn.hide();
33236         this.trashBtn.hide();
33237         
33238         if(this.showDownload){
33239             this.downloadBtn.show();
33240         }
33241         
33242         if(this.showTrash){
33243             this.trashBtn.show();
33244         }
33245         
33246         if(!this.showDownload && !this.showTrash) {
33247             this.footerEl.hide();
33248         }
33249         
33250     },
33251     
33252     initial : function()
33253     {
33254         this.fireEvent('initial', this);
33255         
33256     },
33257     
33258     onClick : function(e)
33259     {
33260         e.preventDefault();
33261         
33262         this.fireEvent('click', this);
33263     },
33264     
33265     onDownload : function(e)
33266     {
33267         e.preventDefault();
33268         
33269         this.fireEvent('download', this);
33270     },
33271     
33272     onTrash : function(e)
33273     {
33274         e.preventDefault();
33275         
33276         this.fireEvent('trash', this);
33277     }
33278     
33279 });
33280 /*
33281  * - LGPL
33282  *
33283  * nav progress bar
33284  * 
33285  */
33286
33287 /**
33288  * @class Roo.bootstrap.NavProgressBar
33289  * @extends Roo.bootstrap.Component
33290  * Bootstrap NavProgressBar class
33291  * 
33292  * @constructor
33293  * Create a new nav progress bar
33294  * @param {Object} config The config object
33295  */
33296
33297 Roo.bootstrap.NavProgressBar = function(config){
33298     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33299
33300     this.bullets = this.bullets || [];
33301    
33302 //    Roo.bootstrap.NavProgressBar.register(this);
33303      this.addEvents({
33304         /**
33305              * @event changed
33306              * Fires when the active item changes
33307              * @param {Roo.bootstrap.NavProgressBar} this
33308              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33309              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33310          */
33311         'changed': true
33312      });
33313     
33314 };
33315
33316 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33317     
33318     bullets : [],
33319     barItems : [],
33320     
33321     getAutoCreate : function()
33322     {
33323         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33324         
33325         cfg = {
33326             tag : 'div',
33327             cls : 'roo-navigation-bar-group',
33328             cn : [
33329                 {
33330                     tag : 'div',
33331                     cls : 'roo-navigation-top-bar'
33332                 },
33333                 {
33334                     tag : 'div',
33335                     cls : 'roo-navigation-bullets-bar',
33336                     cn : [
33337                         {
33338                             tag : 'ul',
33339                             cls : 'roo-navigation-bar'
33340                         }
33341                     ]
33342                 },
33343                 
33344                 {
33345                     tag : 'div',
33346                     cls : 'roo-navigation-bottom-bar'
33347                 }
33348             ]
33349             
33350         };
33351         
33352         return cfg;
33353         
33354     },
33355     
33356     initEvents: function() 
33357     {
33358         
33359     },
33360     
33361     onRender : function(ct, position) 
33362     {
33363         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33364         
33365         if(this.bullets.length){
33366             Roo.each(this.bullets, function(b){
33367                this.addItem(b);
33368             }, this);
33369         }
33370         
33371         this.format();
33372         
33373     },
33374     
33375     addItem : function(cfg)
33376     {
33377         var item = new Roo.bootstrap.NavProgressItem(cfg);
33378         
33379         item.parentId = this.id;
33380         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33381         
33382         if(cfg.html){
33383             var top = new Roo.bootstrap.Element({
33384                 tag : 'div',
33385                 cls : 'roo-navigation-bar-text'
33386             });
33387             
33388             var bottom = new Roo.bootstrap.Element({
33389                 tag : 'div',
33390                 cls : 'roo-navigation-bar-text'
33391             });
33392             
33393             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33394             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33395             
33396             var topText = new Roo.bootstrap.Element({
33397                 tag : 'span',
33398                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33399             });
33400             
33401             var bottomText = new Roo.bootstrap.Element({
33402                 tag : 'span',
33403                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33404             });
33405             
33406             topText.onRender(top.el, null);
33407             bottomText.onRender(bottom.el, null);
33408             
33409             item.topEl = top;
33410             item.bottomEl = bottom;
33411         }
33412         
33413         this.barItems.push(item);
33414         
33415         return item;
33416     },
33417     
33418     getActive : function()
33419     {
33420         var active = false;
33421         
33422         Roo.each(this.barItems, function(v){
33423             
33424             if (!v.isActive()) {
33425                 return;
33426             }
33427             
33428             active = v;
33429             return false;
33430             
33431         });
33432         
33433         return active;
33434     },
33435     
33436     setActiveItem : function(item)
33437     {
33438         var prev = false;
33439         
33440         Roo.each(this.barItems, function(v){
33441             if (v.rid == item.rid) {
33442                 return ;
33443             }
33444             
33445             if (v.isActive()) {
33446                 v.setActive(false);
33447                 prev = v;
33448             }
33449         });
33450
33451         item.setActive(true);
33452         
33453         this.fireEvent('changed', this, item, prev);
33454     },
33455     
33456     getBarItem: function(rid)
33457     {
33458         var ret = false;
33459         
33460         Roo.each(this.barItems, function(e) {
33461             if (e.rid != rid) {
33462                 return;
33463             }
33464             
33465             ret =  e;
33466             return false;
33467         });
33468         
33469         return ret;
33470     },
33471     
33472     indexOfItem : function(item)
33473     {
33474         var index = false;
33475         
33476         Roo.each(this.barItems, function(v, i){
33477             
33478             if (v.rid != item.rid) {
33479                 return;
33480             }
33481             
33482             index = i;
33483             return false
33484         });
33485         
33486         return index;
33487     },
33488     
33489     setActiveNext : function()
33490     {
33491         var i = this.indexOfItem(this.getActive());
33492         
33493         if (i > this.barItems.length) {
33494             return;
33495         }
33496         
33497         this.setActiveItem(this.barItems[i+1]);
33498     },
33499     
33500     setActivePrev : function()
33501     {
33502         var i = this.indexOfItem(this.getActive());
33503         
33504         if (i  < 1) {
33505             return;
33506         }
33507         
33508         this.setActiveItem(this.barItems[i-1]);
33509     },
33510     
33511     format : function()
33512     {
33513         if(!this.barItems.length){
33514             return;
33515         }
33516      
33517         var width = 100 / this.barItems.length;
33518         
33519         Roo.each(this.barItems, function(i){
33520             i.el.setStyle('width', width + '%');
33521             i.topEl.el.setStyle('width', width + '%');
33522             i.bottomEl.el.setStyle('width', width + '%');
33523         }, this);
33524         
33525     }
33526     
33527 });
33528 /*
33529  * - LGPL
33530  *
33531  * Nav Progress Item
33532  * 
33533  */
33534
33535 /**
33536  * @class Roo.bootstrap.NavProgressItem
33537  * @extends Roo.bootstrap.Component
33538  * Bootstrap NavProgressItem class
33539  * @cfg {String} rid the reference id
33540  * @cfg {Boolean} active (true|false) Is item active default false
33541  * @cfg {Boolean} disabled (true|false) Is item active default false
33542  * @cfg {String} html
33543  * @cfg {String} position (top|bottom) text position default bottom
33544  * @cfg {String} icon show icon instead of number
33545  * 
33546  * @constructor
33547  * Create a new NavProgressItem
33548  * @param {Object} config The config object
33549  */
33550 Roo.bootstrap.NavProgressItem = function(config){
33551     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33552     this.addEvents({
33553         // raw events
33554         /**
33555          * @event click
33556          * The raw click event for the entire grid.
33557          * @param {Roo.bootstrap.NavProgressItem} this
33558          * @param {Roo.EventObject} e
33559          */
33560         "click" : true
33561     });
33562    
33563 };
33564
33565 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33566     
33567     rid : '',
33568     active : false,
33569     disabled : false,
33570     html : '',
33571     position : 'bottom',
33572     icon : false,
33573     
33574     getAutoCreate : function()
33575     {
33576         var iconCls = 'roo-navigation-bar-item-icon';
33577         
33578         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33579         
33580         var cfg = {
33581             tag: 'li',
33582             cls: 'roo-navigation-bar-item',
33583             cn : [
33584                 {
33585                     tag : 'i',
33586                     cls : iconCls
33587                 }
33588             ]
33589         };
33590         
33591         if(this.active){
33592             cfg.cls += ' active';
33593         }
33594         if(this.disabled){
33595             cfg.cls += ' disabled';
33596         }
33597         
33598         return cfg;
33599     },
33600     
33601     disable : function()
33602     {
33603         this.setDisabled(true);
33604     },
33605     
33606     enable : function()
33607     {
33608         this.setDisabled(false);
33609     },
33610     
33611     initEvents: function() 
33612     {
33613         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33614         
33615         this.iconEl.on('click', this.onClick, this);
33616     },
33617     
33618     onClick : function(e)
33619     {
33620         e.preventDefault();
33621         
33622         if(this.disabled){
33623             return;
33624         }
33625         
33626         if(this.fireEvent('click', this, e) === false){
33627             return;
33628         };
33629         
33630         this.parent().setActiveItem(this);
33631     },
33632     
33633     isActive: function () 
33634     {
33635         return this.active;
33636     },
33637     
33638     setActive : function(state)
33639     {
33640         if(this.active == state){
33641             return;
33642         }
33643         
33644         this.active = state;
33645         
33646         if (state) {
33647             this.el.addClass('active');
33648             return;
33649         }
33650         
33651         this.el.removeClass('active');
33652         
33653         return;
33654     },
33655     
33656     setDisabled : function(state)
33657     {
33658         if(this.disabled == state){
33659             return;
33660         }
33661         
33662         this.disabled = state;
33663         
33664         if (state) {
33665             this.el.addClass('disabled');
33666             return;
33667         }
33668         
33669         this.el.removeClass('disabled');
33670     },
33671     
33672     tooltipEl : function()
33673     {
33674         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33675     }
33676 });
33677  
33678
33679  /*
33680  * - LGPL
33681  *
33682  * FieldLabel
33683  * 
33684  */
33685
33686 /**
33687  * @class Roo.bootstrap.FieldLabel
33688  * @extends Roo.bootstrap.Component
33689  * Bootstrap FieldLabel class
33690  * @cfg {String} html contents of the element
33691  * @cfg {String} tag tag of the element default label
33692  * @cfg {String} cls class of the element
33693  * @cfg {String} target label target 
33694  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33695  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33696  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33697  * @cfg {String} iconTooltip default "This field is required"
33698  * @cfg {String} indicatorpos (left|right) default left
33699  * 
33700  * @constructor
33701  * Create a new FieldLabel
33702  * @param {Object} config The config object
33703  */
33704
33705 Roo.bootstrap.FieldLabel = function(config){
33706     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33707     
33708     this.addEvents({
33709             /**
33710              * @event invalid
33711              * Fires after the field has been marked as invalid.
33712              * @param {Roo.form.FieldLabel} this
33713              * @param {String} msg The validation message
33714              */
33715             invalid : true,
33716             /**
33717              * @event valid
33718              * Fires after the field has been validated with no errors.
33719              * @param {Roo.form.FieldLabel} this
33720              */
33721             valid : true
33722         });
33723 };
33724
33725 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33726     
33727     tag: 'label',
33728     cls: '',
33729     html: '',
33730     target: '',
33731     allowBlank : true,
33732     invalidClass : 'has-warning',
33733     validClass : 'has-success',
33734     iconTooltip : 'This field is required',
33735     indicatorpos : 'left',
33736     
33737     getAutoCreate : function(){
33738         
33739         var cls = "";
33740         if (!this.allowBlank) {
33741             cls  = "visible";
33742         }
33743         
33744         var cfg = {
33745             tag : this.tag,
33746             cls : 'roo-bootstrap-field-label ' + this.cls,
33747             for : this.target,
33748             cn : [
33749                 {
33750                     tag : 'i',
33751                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33752                     tooltip : this.iconTooltip
33753                 },
33754                 {
33755                     tag : 'span',
33756                     html : this.html
33757                 }
33758             ] 
33759         };
33760         
33761         if(this.indicatorpos == 'right'){
33762             var cfg = {
33763                 tag : this.tag,
33764                 cls : 'roo-bootstrap-field-label ' + this.cls,
33765                 for : this.target,
33766                 cn : [
33767                     {
33768                         tag : 'span',
33769                         html : this.html
33770                     },
33771                     {
33772                         tag : 'i',
33773                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33774                         tooltip : this.iconTooltip
33775                     }
33776                 ] 
33777             };
33778         }
33779         
33780         return cfg;
33781     },
33782     
33783     initEvents: function() 
33784     {
33785         Roo.bootstrap.Element.superclass.initEvents.call(this);
33786         
33787         this.indicator = this.indicatorEl();
33788         
33789         if(this.indicator){
33790             this.indicator.removeClass('visible');
33791             this.indicator.addClass('invisible');
33792         }
33793         
33794         Roo.bootstrap.FieldLabel.register(this);
33795     },
33796     
33797     indicatorEl : function()
33798     {
33799         var indicator = this.el.select('i.roo-required-indicator',true).first();
33800         
33801         if(!indicator){
33802             return false;
33803         }
33804         
33805         return indicator;
33806         
33807     },
33808     
33809     /**
33810      * Mark this field as valid
33811      */
33812     markValid : function()
33813     {
33814         if(this.indicator){
33815             this.indicator.removeClass('visible');
33816             this.indicator.addClass('invisible');
33817         }
33818         if (Roo.bootstrap.version == 3) {
33819             this.el.removeClass(this.invalidClass);
33820             this.el.addClass(this.validClass);
33821         } else {
33822             this.el.removeClass('is-invalid');
33823             this.el.addClass('is-valid');
33824         }
33825         
33826         
33827         this.fireEvent('valid', this);
33828     },
33829     
33830     /**
33831      * Mark this field as invalid
33832      * @param {String} msg The validation message
33833      */
33834     markInvalid : function(msg)
33835     {
33836         if(this.indicator){
33837             this.indicator.removeClass('invisible');
33838             this.indicator.addClass('visible');
33839         }
33840           if (Roo.bootstrap.version == 3) {
33841             this.el.removeClass(this.validClass);
33842             this.el.addClass(this.invalidClass);
33843         } else {
33844             this.el.removeClass('is-valid');
33845             this.el.addClass('is-invalid');
33846         }
33847         
33848         
33849         this.fireEvent('invalid', this, msg);
33850     }
33851     
33852    
33853 });
33854
33855 Roo.apply(Roo.bootstrap.FieldLabel, {
33856     
33857     groups: {},
33858     
33859      /**
33860     * register a FieldLabel Group
33861     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33862     */
33863     register : function(label)
33864     {
33865         if(this.groups.hasOwnProperty(label.target)){
33866             return;
33867         }
33868      
33869         this.groups[label.target] = label;
33870         
33871     },
33872     /**
33873     * fetch a FieldLabel Group based on the target
33874     * @param {string} target
33875     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33876     */
33877     get: function(target) {
33878         if (typeof(this.groups[target]) == 'undefined') {
33879             return false;
33880         }
33881         
33882         return this.groups[target] ;
33883     }
33884 });
33885
33886  
33887
33888  /*
33889  * - LGPL
33890  *
33891  * page DateSplitField.
33892  * 
33893  */
33894
33895
33896 /**
33897  * @class Roo.bootstrap.DateSplitField
33898  * @extends Roo.bootstrap.Component
33899  * Bootstrap DateSplitField class
33900  * @cfg {string} fieldLabel - the label associated
33901  * @cfg {Number} labelWidth set the width of label (0-12)
33902  * @cfg {String} labelAlign (top|left)
33903  * @cfg {Boolean} dayAllowBlank (true|false) default false
33904  * @cfg {Boolean} monthAllowBlank (true|false) default false
33905  * @cfg {Boolean} yearAllowBlank (true|false) default false
33906  * @cfg {string} dayPlaceholder 
33907  * @cfg {string} monthPlaceholder
33908  * @cfg {string} yearPlaceholder
33909  * @cfg {string} dayFormat default 'd'
33910  * @cfg {string} monthFormat default 'm'
33911  * @cfg {string} yearFormat default 'Y'
33912  * @cfg {Number} labellg set the width of label (1-12)
33913  * @cfg {Number} labelmd set the width of label (1-12)
33914  * @cfg {Number} labelsm set the width of label (1-12)
33915  * @cfg {Number} labelxs set the width of label (1-12)
33916
33917  *     
33918  * @constructor
33919  * Create a new DateSplitField
33920  * @param {Object} config The config object
33921  */
33922
33923 Roo.bootstrap.DateSplitField = function(config){
33924     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33925     
33926     this.addEvents({
33927         // raw events
33928          /**
33929          * @event years
33930          * getting the data of years
33931          * @param {Roo.bootstrap.DateSplitField} this
33932          * @param {Object} years
33933          */
33934         "years" : true,
33935         /**
33936          * @event days
33937          * getting the data of days
33938          * @param {Roo.bootstrap.DateSplitField} this
33939          * @param {Object} days
33940          */
33941         "days" : true,
33942         /**
33943          * @event invalid
33944          * Fires after the field has been marked as invalid.
33945          * @param {Roo.form.Field} this
33946          * @param {String} msg The validation message
33947          */
33948         invalid : true,
33949        /**
33950          * @event valid
33951          * Fires after the field has been validated with no errors.
33952          * @param {Roo.form.Field} this
33953          */
33954         valid : true
33955     });
33956 };
33957
33958 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33959     
33960     fieldLabel : '',
33961     labelAlign : 'top',
33962     labelWidth : 3,
33963     dayAllowBlank : false,
33964     monthAllowBlank : false,
33965     yearAllowBlank : false,
33966     dayPlaceholder : '',
33967     monthPlaceholder : '',
33968     yearPlaceholder : '',
33969     dayFormat : 'd',
33970     monthFormat : 'm',
33971     yearFormat : 'Y',
33972     isFormField : true,
33973     labellg : 0,
33974     labelmd : 0,
33975     labelsm : 0,
33976     labelxs : 0,
33977     
33978     getAutoCreate : function()
33979     {
33980         var cfg = {
33981             tag : 'div',
33982             cls : 'row roo-date-split-field-group',
33983             cn : [
33984                 {
33985                     tag : 'input',
33986                     type : 'hidden',
33987                     cls : 'form-hidden-field roo-date-split-field-group-value',
33988                     name : this.name
33989                 }
33990             ]
33991         };
33992         
33993         var labelCls = 'col-md-12';
33994         var contentCls = 'col-md-4';
33995         
33996         if(this.fieldLabel){
33997             
33998             var label = {
33999                 tag : 'div',
34000                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34001                 cn : [
34002                     {
34003                         tag : 'label',
34004                         html : this.fieldLabel
34005                     }
34006                 ]
34007             };
34008             
34009             if(this.labelAlign == 'left'){
34010             
34011                 if(this.labelWidth > 12){
34012                     label.style = "width: " + this.labelWidth + 'px';
34013                 }
34014
34015                 if(this.labelWidth < 13 && this.labelmd == 0){
34016                     this.labelmd = this.labelWidth;
34017                 }
34018
34019                 if(this.labellg > 0){
34020                     labelCls = ' col-lg-' + this.labellg;
34021                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34022                 }
34023
34024                 if(this.labelmd > 0){
34025                     labelCls = ' col-md-' + this.labelmd;
34026                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34027                 }
34028
34029                 if(this.labelsm > 0){
34030                     labelCls = ' col-sm-' + this.labelsm;
34031                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34032                 }
34033
34034                 if(this.labelxs > 0){
34035                     labelCls = ' col-xs-' + this.labelxs;
34036                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34037                 }
34038             }
34039             
34040             label.cls += ' ' + labelCls;
34041             
34042             cfg.cn.push(label);
34043         }
34044         
34045         Roo.each(['day', 'month', 'year'], function(t){
34046             cfg.cn.push({
34047                 tag : 'div',
34048                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34049             });
34050         }, this);
34051         
34052         return cfg;
34053     },
34054     
34055     inputEl: function ()
34056     {
34057         return this.el.select('.roo-date-split-field-group-value', true).first();
34058     },
34059     
34060     onRender : function(ct, position) 
34061     {
34062         var _this = this;
34063         
34064         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34065         
34066         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34067         
34068         this.dayField = new Roo.bootstrap.ComboBox({
34069             allowBlank : this.dayAllowBlank,
34070             alwaysQuery : true,
34071             displayField : 'value',
34072             editable : false,
34073             fieldLabel : '',
34074             forceSelection : true,
34075             mode : 'local',
34076             placeholder : this.dayPlaceholder,
34077             selectOnFocus : true,
34078             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34079             triggerAction : 'all',
34080             typeAhead : true,
34081             valueField : 'value',
34082             store : new Roo.data.SimpleStore({
34083                 data : (function() {    
34084                     var days = [];
34085                     _this.fireEvent('days', _this, days);
34086                     return days;
34087                 })(),
34088                 fields : [ 'value' ]
34089             }),
34090             listeners : {
34091                 select : function (_self, record, index)
34092                 {
34093                     _this.setValue(_this.getValue());
34094                 }
34095             }
34096         });
34097
34098         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34099         
34100         this.monthField = new Roo.bootstrap.MonthField({
34101             after : '<i class=\"fa fa-calendar\"></i>',
34102             allowBlank : this.monthAllowBlank,
34103             placeholder : this.monthPlaceholder,
34104             readOnly : true,
34105             listeners : {
34106                 render : function (_self)
34107                 {
34108                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34109                         e.preventDefault();
34110                         _self.focus();
34111                     });
34112                 },
34113                 select : function (_self, oldvalue, newvalue)
34114                 {
34115                     _this.setValue(_this.getValue());
34116                 }
34117             }
34118         });
34119         
34120         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34121         
34122         this.yearField = new Roo.bootstrap.ComboBox({
34123             allowBlank : this.yearAllowBlank,
34124             alwaysQuery : true,
34125             displayField : 'value',
34126             editable : false,
34127             fieldLabel : '',
34128             forceSelection : true,
34129             mode : 'local',
34130             placeholder : this.yearPlaceholder,
34131             selectOnFocus : true,
34132             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34133             triggerAction : 'all',
34134             typeAhead : true,
34135             valueField : 'value',
34136             store : new Roo.data.SimpleStore({
34137                 data : (function() {
34138                     var years = [];
34139                     _this.fireEvent('years', _this, years);
34140                     return years;
34141                 })(),
34142                 fields : [ 'value' ]
34143             }),
34144             listeners : {
34145                 select : function (_self, record, index)
34146                 {
34147                     _this.setValue(_this.getValue());
34148                 }
34149             }
34150         });
34151
34152         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34153     },
34154     
34155     setValue : function(v, format)
34156     {
34157         this.inputEl.dom.value = v;
34158         
34159         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34160         
34161         var d = Date.parseDate(v, f);
34162         
34163         if(!d){
34164             this.validate();
34165             return;
34166         }
34167         
34168         this.setDay(d.format(this.dayFormat));
34169         this.setMonth(d.format(this.monthFormat));
34170         this.setYear(d.format(this.yearFormat));
34171         
34172         this.validate();
34173         
34174         return;
34175     },
34176     
34177     setDay : function(v)
34178     {
34179         this.dayField.setValue(v);
34180         this.inputEl.dom.value = this.getValue();
34181         this.validate();
34182         return;
34183     },
34184     
34185     setMonth : function(v)
34186     {
34187         this.monthField.setValue(v, true);
34188         this.inputEl.dom.value = this.getValue();
34189         this.validate();
34190         return;
34191     },
34192     
34193     setYear : function(v)
34194     {
34195         this.yearField.setValue(v);
34196         this.inputEl.dom.value = this.getValue();
34197         this.validate();
34198         return;
34199     },
34200     
34201     getDay : function()
34202     {
34203         return this.dayField.getValue();
34204     },
34205     
34206     getMonth : function()
34207     {
34208         return this.monthField.getValue();
34209     },
34210     
34211     getYear : function()
34212     {
34213         return this.yearField.getValue();
34214     },
34215     
34216     getValue : function()
34217     {
34218         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34219         
34220         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34221         
34222         return date;
34223     },
34224     
34225     reset : function()
34226     {
34227         this.setDay('');
34228         this.setMonth('');
34229         this.setYear('');
34230         this.inputEl.dom.value = '';
34231         this.validate();
34232         return;
34233     },
34234     
34235     validate : function()
34236     {
34237         var d = this.dayField.validate();
34238         var m = this.monthField.validate();
34239         var y = this.yearField.validate();
34240         
34241         var valid = true;
34242         
34243         if(
34244                 (!this.dayAllowBlank && !d) ||
34245                 (!this.monthAllowBlank && !m) ||
34246                 (!this.yearAllowBlank && !y)
34247         ){
34248             valid = false;
34249         }
34250         
34251         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34252             return valid;
34253         }
34254         
34255         if(valid){
34256             this.markValid();
34257             return valid;
34258         }
34259         
34260         this.markInvalid();
34261         
34262         return valid;
34263     },
34264     
34265     markValid : function()
34266     {
34267         
34268         var label = this.el.select('label', true).first();
34269         var icon = this.el.select('i.fa-star', true).first();
34270
34271         if(label && icon){
34272             icon.remove();
34273         }
34274         
34275         this.fireEvent('valid', this);
34276     },
34277     
34278      /**
34279      * Mark this field as invalid
34280      * @param {String} msg The validation message
34281      */
34282     markInvalid : function(msg)
34283     {
34284         
34285         var label = this.el.select('label', true).first();
34286         var icon = this.el.select('i.fa-star', true).first();
34287
34288         if(label && !icon){
34289             this.el.select('.roo-date-split-field-label', true).createChild({
34290                 tag : 'i',
34291                 cls : 'text-danger fa fa-lg fa-star',
34292                 tooltip : 'This field is required',
34293                 style : 'margin-right:5px;'
34294             }, label, true);
34295         }
34296         
34297         this.fireEvent('invalid', this, msg);
34298     },
34299     
34300     clearInvalid : function()
34301     {
34302         var label = this.el.select('label', true).first();
34303         var icon = this.el.select('i.fa-star', true).first();
34304
34305         if(label && icon){
34306             icon.remove();
34307         }
34308         
34309         this.fireEvent('valid', this);
34310     },
34311     
34312     getName: function()
34313     {
34314         return this.name;
34315     }
34316     
34317 });
34318
34319  /**
34320  *
34321  * This is based on 
34322  * http://masonry.desandro.com
34323  *
34324  * The idea is to render all the bricks based on vertical width...
34325  *
34326  * The original code extends 'outlayer' - we might need to use that....
34327  * 
34328  */
34329
34330
34331 /**
34332  * @class Roo.bootstrap.LayoutMasonry
34333  * @extends Roo.bootstrap.Component
34334  * Bootstrap Layout Masonry class
34335  * 
34336  * @constructor
34337  * Create a new Element
34338  * @param {Object} config The config object
34339  */
34340
34341 Roo.bootstrap.LayoutMasonry = function(config){
34342     
34343     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34344     
34345     this.bricks = [];
34346     
34347     Roo.bootstrap.LayoutMasonry.register(this);
34348     
34349     this.addEvents({
34350         // raw events
34351         /**
34352          * @event layout
34353          * Fire after layout the items
34354          * @param {Roo.bootstrap.LayoutMasonry} this
34355          * @param {Roo.EventObject} e
34356          */
34357         "layout" : true
34358     });
34359     
34360 };
34361
34362 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34363     
34364     /**
34365      * @cfg {Boolean} isLayoutInstant = no animation?
34366      */   
34367     isLayoutInstant : false, // needed?
34368    
34369     /**
34370      * @cfg {Number} boxWidth  width of the columns
34371      */   
34372     boxWidth : 450,
34373     
34374       /**
34375      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34376      */   
34377     boxHeight : 0,
34378     
34379     /**
34380      * @cfg {Number} padWidth padding below box..
34381      */   
34382     padWidth : 10, 
34383     
34384     /**
34385      * @cfg {Number} gutter gutter width..
34386      */   
34387     gutter : 10,
34388     
34389      /**
34390      * @cfg {Number} maxCols maximum number of columns
34391      */   
34392     
34393     maxCols: 0,
34394     
34395     /**
34396      * @cfg {Boolean} isAutoInitial defalut true
34397      */   
34398     isAutoInitial : true, 
34399     
34400     containerWidth: 0,
34401     
34402     /**
34403      * @cfg {Boolean} isHorizontal defalut false
34404      */   
34405     isHorizontal : false, 
34406
34407     currentSize : null,
34408     
34409     tag: 'div',
34410     
34411     cls: '',
34412     
34413     bricks: null, //CompositeElement
34414     
34415     cols : 1,
34416     
34417     _isLayoutInited : false,
34418     
34419 //    isAlternative : false, // only use for vertical layout...
34420     
34421     /**
34422      * @cfg {Number} alternativePadWidth padding below box..
34423      */   
34424     alternativePadWidth : 50,
34425     
34426     selectedBrick : [],
34427     
34428     getAutoCreate : function(){
34429         
34430         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34431         
34432         var cfg = {
34433             tag: this.tag,
34434             cls: 'blog-masonary-wrapper ' + this.cls,
34435             cn : {
34436                 cls : 'mas-boxes masonary'
34437             }
34438         };
34439         
34440         return cfg;
34441     },
34442     
34443     getChildContainer: function( )
34444     {
34445         if (this.boxesEl) {
34446             return this.boxesEl;
34447         }
34448         
34449         this.boxesEl = this.el.select('.mas-boxes').first();
34450         
34451         return this.boxesEl;
34452     },
34453     
34454     
34455     initEvents : function()
34456     {
34457         var _this = this;
34458         
34459         if(this.isAutoInitial){
34460             Roo.log('hook children rendered');
34461             this.on('childrenrendered', function() {
34462                 Roo.log('children rendered');
34463                 _this.initial();
34464             } ,this);
34465         }
34466     },
34467     
34468     initial : function()
34469     {
34470         this.selectedBrick = [];
34471         
34472         this.currentSize = this.el.getBox(true);
34473         
34474         Roo.EventManager.onWindowResize(this.resize, this); 
34475
34476         if(!this.isAutoInitial){
34477             this.layout();
34478             return;
34479         }
34480         
34481         this.layout();
34482         
34483         return;
34484         //this.layout.defer(500,this);
34485         
34486     },
34487     
34488     resize : function()
34489     {
34490         var cs = this.el.getBox(true);
34491         
34492         if (
34493                 this.currentSize.width == cs.width && 
34494                 this.currentSize.x == cs.x && 
34495                 this.currentSize.height == cs.height && 
34496                 this.currentSize.y == cs.y 
34497         ) {
34498             Roo.log("no change in with or X or Y");
34499             return;
34500         }
34501         
34502         this.currentSize = cs;
34503         
34504         this.layout();
34505         
34506     },
34507     
34508     layout : function()
34509     {   
34510         this._resetLayout();
34511         
34512         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34513         
34514         this.layoutItems( isInstant );
34515       
34516         this._isLayoutInited = true;
34517         
34518         this.fireEvent('layout', this);
34519         
34520     },
34521     
34522     _resetLayout : function()
34523     {
34524         if(this.isHorizontal){
34525             this.horizontalMeasureColumns();
34526             return;
34527         }
34528         
34529         this.verticalMeasureColumns();
34530         
34531     },
34532     
34533     verticalMeasureColumns : function()
34534     {
34535         this.getContainerWidth();
34536         
34537 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34538 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34539 //            return;
34540 //        }
34541         
34542         var boxWidth = this.boxWidth + this.padWidth;
34543         
34544         if(this.containerWidth < this.boxWidth){
34545             boxWidth = this.containerWidth
34546         }
34547         
34548         var containerWidth = this.containerWidth;
34549         
34550         var cols = Math.floor(containerWidth / boxWidth);
34551         
34552         this.cols = Math.max( cols, 1 );
34553         
34554         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34555         
34556         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34557         
34558         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34559         
34560         this.colWidth = boxWidth + avail - this.padWidth;
34561         
34562         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34563         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34564     },
34565     
34566     horizontalMeasureColumns : function()
34567     {
34568         this.getContainerWidth();
34569         
34570         var boxWidth = this.boxWidth;
34571         
34572         if(this.containerWidth < boxWidth){
34573             boxWidth = this.containerWidth;
34574         }
34575         
34576         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34577         
34578         this.el.setHeight(boxWidth);
34579         
34580     },
34581     
34582     getContainerWidth : function()
34583     {
34584         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34585     },
34586     
34587     layoutItems : function( isInstant )
34588     {
34589         Roo.log(this.bricks);
34590         
34591         var items = Roo.apply([], this.bricks);
34592         
34593         if(this.isHorizontal){
34594             this._horizontalLayoutItems( items , isInstant );
34595             return;
34596         }
34597         
34598 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34599 //            this._verticalAlternativeLayoutItems( items , isInstant );
34600 //            return;
34601 //        }
34602         
34603         this._verticalLayoutItems( items , isInstant );
34604         
34605     },
34606     
34607     _verticalLayoutItems : function ( items , isInstant)
34608     {
34609         if ( !items || !items.length ) {
34610             return;
34611         }
34612         
34613         var standard = [
34614             ['xs', 'xs', 'xs', 'tall'],
34615             ['xs', 'xs', 'tall'],
34616             ['xs', 'xs', 'sm'],
34617             ['xs', 'xs', 'xs'],
34618             ['xs', 'tall'],
34619             ['xs', 'sm'],
34620             ['xs', 'xs'],
34621             ['xs'],
34622             
34623             ['sm', 'xs', 'xs'],
34624             ['sm', 'xs'],
34625             ['sm'],
34626             
34627             ['tall', 'xs', 'xs', 'xs'],
34628             ['tall', 'xs', 'xs'],
34629             ['tall', 'xs'],
34630             ['tall']
34631             
34632         ];
34633         
34634         var queue = [];
34635         
34636         var boxes = [];
34637         
34638         var box = [];
34639         
34640         Roo.each(items, function(item, k){
34641             
34642             switch (item.size) {
34643                 // these layouts take up a full box,
34644                 case 'md' :
34645                 case 'md-left' :
34646                 case 'md-right' :
34647                 case 'wide' :
34648                     
34649                     if(box.length){
34650                         boxes.push(box);
34651                         box = [];
34652                     }
34653                     
34654                     boxes.push([item]);
34655                     
34656                     break;
34657                     
34658                 case 'xs' :
34659                 case 'sm' :
34660                 case 'tall' :
34661                     
34662                     box.push(item);
34663                     
34664                     break;
34665                 default :
34666                     break;
34667                     
34668             }
34669             
34670         }, this);
34671         
34672         if(box.length){
34673             boxes.push(box);
34674             box = [];
34675         }
34676         
34677         var filterPattern = function(box, length)
34678         {
34679             if(!box.length){
34680                 return;
34681             }
34682             
34683             var match = false;
34684             
34685             var pattern = box.slice(0, length);
34686             
34687             var format = [];
34688             
34689             Roo.each(pattern, function(i){
34690                 format.push(i.size);
34691             }, this);
34692             
34693             Roo.each(standard, function(s){
34694                 
34695                 if(String(s) != String(format)){
34696                     return;
34697                 }
34698                 
34699                 match = true;
34700                 return false;
34701                 
34702             }, this);
34703             
34704             if(!match && length == 1){
34705                 return;
34706             }
34707             
34708             if(!match){
34709                 filterPattern(box, length - 1);
34710                 return;
34711             }
34712                 
34713             queue.push(pattern);
34714
34715             box = box.slice(length, box.length);
34716
34717             filterPattern(box, 4);
34718
34719             return;
34720             
34721         }
34722         
34723         Roo.each(boxes, function(box, k){
34724             
34725             if(!box.length){
34726                 return;
34727             }
34728             
34729             if(box.length == 1){
34730                 queue.push(box);
34731                 return;
34732             }
34733             
34734             filterPattern(box, 4);
34735             
34736         }, this);
34737         
34738         this._processVerticalLayoutQueue( queue, isInstant );
34739         
34740     },
34741     
34742 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34743 //    {
34744 //        if ( !items || !items.length ) {
34745 //            return;
34746 //        }
34747 //
34748 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34749 //        
34750 //    },
34751     
34752     _horizontalLayoutItems : function ( items , isInstant)
34753     {
34754         if ( !items || !items.length || items.length < 3) {
34755             return;
34756         }
34757         
34758         items.reverse();
34759         
34760         var eItems = items.slice(0, 3);
34761         
34762         items = items.slice(3, items.length);
34763         
34764         var standard = [
34765             ['xs', 'xs', 'xs', 'wide'],
34766             ['xs', 'xs', 'wide'],
34767             ['xs', 'xs', 'sm'],
34768             ['xs', 'xs', 'xs'],
34769             ['xs', 'wide'],
34770             ['xs', 'sm'],
34771             ['xs', 'xs'],
34772             ['xs'],
34773             
34774             ['sm', 'xs', 'xs'],
34775             ['sm', 'xs'],
34776             ['sm'],
34777             
34778             ['wide', 'xs', 'xs', 'xs'],
34779             ['wide', 'xs', 'xs'],
34780             ['wide', 'xs'],
34781             ['wide'],
34782             
34783             ['wide-thin']
34784         ];
34785         
34786         var queue = [];
34787         
34788         var boxes = [];
34789         
34790         var box = [];
34791         
34792         Roo.each(items, function(item, k){
34793             
34794             switch (item.size) {
34795                 case 'md' :
34796                 case 'md-left' :
34797                 case 'md-right' :
34798                 case 'tall' :
34799                     
34800                     if(box.length){
34801                         boxes.push(box);
34802                         box = [];
34803                     }
34804                     
34805                     boxes.push([item]);
34806                     
34807                     break;
34808                     
34809                 case 'xs' :
34810                 case 'sm' :
34811                 case 'wide' :
34812                 case 'wide-thin' :
34813                     
34814                     box.push(item);
34815                     
34816                     break;
34817                 default :
34818                     break;
34819                     
34820             }
34821             
34822         }, this);
34823         
34824         if(box.length){
34825             boxes.push(box);
34826             box = [];
34827         }
34828         
34829         var filterPattern = function(box, length)
34830         {
34831             if(!box.length){
34832                 return;
34833             }
34834             
34835             var match = false;
34836             
34837             var pattern = box.slice(0, length);
34838             
34839             var format = [];
34840             
34841             Roo.each(pattern, function(i){
34842                 format.push(i.size);
34843             }, this);
34844             
34845             Roo.each(standard, function(s){
34846                 
34847                 if(String(s) != String(format)){
34848                     return;
34849                 }
34850                 
34851                 match = true;
34852                 return false;
34853                 
34854             }, this);
34855             
34856             if(!match && length == 1){
34857                 return;
34858             }
34859             
34860             if(!match){
34861                 filterPattern(box, length - 1);
34862                 return;
34863             }
34864                 
34865             queue.push(pattern);
34866
34867             box = box.slice(length, box.length);
34868
34869             filterPattern(box, 4);
34870
34871             return;
34872             
34873         }
34874         
34875         Roo.each(boxes, function(box, k){
34876             
34877             if(!box.length){
34878                 return;
34879             }
34880             
34881             if(box.length == 1){
34882                 queue.push(box);
34883                 return;
34884             }
34885             
34886             filterPattern(box, 4);
34887             
34888         }, this);
34889         
34890         
34891         var prune = [];
34892         
34893         var pos = this.el.getBox(true);
34894         
34895         var minX = pos.x;
34896         
34897         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34898         
34899         var hit_end = false;
34900         
34901         Roo.each(queue, function(box){
34902             
34903             if(hit_end){
34904                 
34905                 Roo.each(box, function(b){
34906                 
34907                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34908                     b.el.hide();
34909
34910                 }, this);
34911
34912                 return;
34913             }
34914             
34915             var mx = 0;
34916             
34917             Roo.each(box, function(b){
34918                 
34919                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34920                 b.el.show();
34921
34922                 mx = Math.max(mx, b.x);
34923                 
34924             }, this);
34925             
34926             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34927             
34928             if(maxX < minX){
34929                 
34930                 Roo.each(box, function(b){
34931                 
34932                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34933                     b.el.hide();
34934                     
34935                 }, this);
34936                 
34937                 hit_end = true;
34938                 
34939                 return;
34940             }
34941             
34942             prune.push(box);
34943             
34944         }, this);
34945         
34946         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34947     },
34948     
34949     /** Sets position of item in DOM
34950     * @param {Element} item
34951     * @param {Number} x - horizontal position
34952     * @param {Number} y - vertical position
34953     * @param {Boolean} isInstant - disables transitions
34954     */
34955     _processVerticalLayoutQueue : function( queue, isInstant )
34956     {
34957         var pos = this.el.getBox(true);
34958         var x = pos.x;
34959         var y = pos.y;
34960         var maxY = [];
34961         
34962         for (var i = 0; i < this.cols; i++){
34963             maxY[i] = pos.y;
34964         }
34965         
34966         Roo.each(queue, function(box, k){
34967             
34968             var col = k % this.cols;
34969             
34970             Roo.each(box, function(b,kk){
34971                 
34972                 b.el.position('absolute');
34973                 
34974                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34975                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34976                 
34977                 if(b.size == 'md-left' || b.size == 'md-right'){
34978                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34979                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34980                 }
34981                 
34982                 b.el.setWidth(width);
34983                 b.el.setHeight(height);
34984                 // iframe?
34985                 b.el.select('iframe',true).setSize(width,height);
34986                 
34987             }, this);
34988             
34989             for (var i = 0; i < this.cols; i++){
34990                 
34991                 if(maxY[i] < maxY[col]){
34992                     col = i;
34993                     continue;
34994                 }
34995                 
34996                 col = Math.min(col, i);
34997                 
34998             }
34999             
35000             x = pos.x + col * (this.colWidth + this.padWidth);
35001             
35002             y = maxY[col];
35003             
35004             var positions = [];
35005             
35006             switch (box.length){
35007                 case 1 :
35008                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35009                     break;
35010                 case 2 :
35011                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35012                     break;
35013                 case 3 :
35014                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35015                     break;
35016                 case 4 :
35017                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35018                     break;
35019                 default :
35020                     break;
35021             }
35022             
35023             Roo.each(box, function(b,kk){
35024                 
35025                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35026                 
35027                 var sz = b.el.getSize();
35028                 
35029                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35030                 
35031             }, this);
35032             
35033         }, this);
35034         
35035         var mY = 0;
35036         
35037         for (var i = 0; i < this.cols; i++){
35038             mY = Math.max(mY, maxY[i]);
35039         }
35040         
35041         this.el.setHeight(mY - pos.y);
35042         
35043     },
35044     
35045 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35046 //    {
35047 //        var pos = this.el.getBox(true);
35048 //        var x = pos.x;
35049 //        var y = pos.y;
35050 //        var maxX = pos.right;
35051 //        
35052 //        var maxHeight = 0;
35053 //        
35054 //        Roo.each(items, function(item, k){
35055 //            
35056 //            var c = k % 2;
35057 //            
35058 //            item.el.position('absolute');
35059 //                
35060 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35061 //
35062 //            item.el.setWidth(width);
35063 //
35064 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35065 //
35066 //            item.el.setHeight(height);
35067 //            
35068 //            if(c == 0){
35069 //                item.el.setXY([x, y], isInstant ? false : true);
35070 //            } else {
35071 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35072 //            }
35073 //            
35074 //            y = y + height + this.alternativePadWidth;
35075 //            
35076 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35077 //            
35078 //        }, this);
35079 //        
35080 //        this.el.setHeight(maxHeight);
35081 //        
35082 //    },
35083     
35084     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35085     {
35086         var pos = this.el.getBox(true);
35087         
35088         var minX = pos.x;
35089         var minY = pos.y;
35090         
35091         var maxX = pos.right;
35092         
35093         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35094         
35095         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35096         
35097         Roo.each(queue, function(box, k){
35098             
35099             Roo.each(box, function(b, kk){
35100                 
35101                 b.el.position('absolute');
35102                 
35103                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35104                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35105                 
35106                 if(b.size == 'md-left' || b.size == 'md-right'){
35107                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35108                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35109                 }
35110                 
35111                 b.el.setWidth(width);
35112                 b.el.setHeight(height);
35113                 
35114             }, this);
35115             
35116             if(!box.length){
35117                 return;
35118             }
35119             
35120             var positions = [];
35121             
35122             switch (box.length){
35123                 case 1 :
35124                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35125                     break;
35126                 case 2 :
35127                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35128                     break;
35129                 case 3 :
35130                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35131                     break;
35132                 case 4 :
35133                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35134                     break;
35135                 default :
35136                     break;
35137             }
35138             
35139             Roo.each(box, function(b,kk){
35140                 
35141                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35142                 
35143                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35144                 
35145             }, this);
35146             
35147         }, this);
35148         
35149     },
35150     
35151     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35152     {
35153         Roo.each(eItems, function(b,k){
35154             
35155             b.size = (k == 0) ? 'sm' : 'xs';
35156             b.x = (k == 0) ? 2 : 1;
35157             b.y = (k == 0) ? 2 : 1;
35158             
35159             b.el.position('absolute');
35160             
35161             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35162                 
35163             b.el.setWidth(width);
35164             
35165             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35166             
35167             b.el.setHeight(height);
35168             
35169         }, this);
35170
35171         var positions = [];
35172         
35173         positions.push({
35174             x : maxX - this.unitWidth * 2 - this.gutter,
35175             y : minY
35176         });
35177         
35178         positions.push({
35179             x : maxX - this.unitWidth,
35180             y : minY + (this.unitWidth + this.gutter) * 2
35181         });
35182         
35183         positions.push({
35184             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35185             y : minY
35186         });
35187         
35188         Roo.each(eItems, function(b,k){
35189             
35190             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35191
35192         }, this);
35193         
35194     },
35195     
35196     getVerticalOneBoxColPositions : function(x, y, box)
35197     {
35198         var pos = [];
35199         
35200         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35201         
35202         if(box[0].size == 'md-left'){
35203             rand = 0;
35204         }
35205         
35206         if(box[0].size == 'md-right'){
35207             rand = 1;
35208         }
35209         
35210         pos.push({
35211             x : x + (this.unitWidth + this.gutter) * rand,
35212             y : y
35213         });
35214         
35215         return pos;
35216     },
35217     
35218     getVerticalTwoBoxColPositions : function(x, y, box)
35219     {
35220         var pos = [];
35221         
35222         if(box[0].size == 'xs'){
35223             
35224             pos.push({
35225                 x : x,
35226                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35227             });
35228
35229             pos.push({
35230                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35231                 y : y
35232             });
35233             
35234             return pos;
35235             
35236         }
35237         
35238         pos.push({
35239             x : x,
35240             y : y
35241         });
35242
35243         pos.push({
35244             x : x + (this.unitWidth + this.gutter) * 2,
35245             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35246         });
35247         
35248         return pos;
35249         
35250     },
35251     
35252     getVerticalThreeBoxColPositions : function(x, y, box)
35253     {
35254         var pos = [];
35255         
35256         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35257             
35258             pos.push({
35259                 x : x,
35260                 y : y
35261             });
35262
35263             pos.push({
35264                 x : x + (this.unitWidth + this.gutter) * 1,
35265                 y : y
35266             });
35267             
35268             pos.push({
35269                 x : x + (this.unitWidth + this.gutter) * 2,
35270                 y : y
35271             });
35272             
35273             return pos;
35274             
35275         }
35276         
35277         if(box[0].size == 'xs' && box[1].size == 'xs'){
35278             
35279             pos.push({
35280                 x : x,
35281                 y : y
35282             });
35283
35284             pos.push({
35285                 x : x,
35286                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35287             });
35288             
35289             pos.push({
35290                 x : x + (this.unitWidth + this.gutter) * 1,
35291                 y : y
35292             });
35293             
35294             return pos;
35295             
35296         }
35297         
35298         pos.push({
35299             x : x,
35300             y : y
35301         });
35302
35303         pos.push({
35304             x : x + (this.unitWidth + this.gutter) * 2,
35305             y : y
35306         });
35307
35308         pos.push({
35309             x : x + (this.unitWidth + this.gutter) * 2,
35310             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35311         });
35312             
35313         return pos;
35314         
35315     },
35316     
35317     getVerticalFourBoxColPositions : function(x, y, box)
35318     {
35319         var pos = [];
35320         
35321         if(box[0].size == 'xs'){
35322             
35323             pos.push({
35324                 x : x,
35325                 y : y
35326             });
35327
35328             pos.push({
35329                 x : x,
35330                 y : y + (this.unitHeight + this.gutter) * 1
35331             });
35332             
35333             pos.push({
35334                 x : x,
35335                 y : y + (this.unitHeight + this.gutter) * 2
35336             });
35337             
35338             pos.push({
35339                 x : x + (this.unitWidth + this.gutter) * 1,
35340                 y : y
35341             });
35342             
35343             return pos;
35344             
35345         }
35346         
35347         pos.push({
35348             x : x,
35349             y : y
35350         });
35351
35352         pos.push({
35353             x : x + (this.unitWidth + this.gutter) * 2,
35354             y : y
35355         });
35356
35357         pos.push({
35358             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35359             y : y + (this.unitHeight + this.gutter) * 1
35360         });
35361
35362         pos.push({
35363             x : x + (this.unitWidth + this.gutter) * 2,
35364             y : y + (this.unitWidth + this.gutter) * 2
35365         });
35366
35367         return pos;
35368         
35369     },
35370     
35371     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35372     {
35373         var pos = [];
35374         
35375         if(box[0].size == 'md-left'){
35376             pos.push({
35377                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35378                 y : minY
35379             });
35380             
35381             return pos;
35382         }
35383         
35384         if(box[0].size == 'md-right'){
35385             pos.push({
35386                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35387                 y : minY + (this.unitWidth + this.gutter) * 1
35388             });
35389             
35390             return pos;
35391         }
35392         
35393         var rand = Math.floor(Math.random() * (4 - box[0].y));
35394         
35395         pos.push({
35396             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35397             y : minY + (this.unitWidth + this.gutter) * rand
35398         });
35399         
35400         return pos;
35401         
35402     },
35403     
35404     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35405     {
35406         var pos = [];
35407         
35408         if(box[0].size == 'xs'){
35409             
35410             pos.push({
35411                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35412                 y : minY
35413             });
35414
35415             pos.push({
35416                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35417                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35418             });
35419             
35420             return pos;
35421             
35422         }
35423         
35424         pos.push({
35425             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35426             y : minY
35427         });
35428
35429         pos.push({
35430             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35431             y : minY + (this.unitWidth + this.gutter) * 2
35432         });
35433         
35434         return pos;
35435         
35436     },
35437     
35438     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35439     {
35440         var pos = [];
35441         
35442         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35443             
35444             pos.push({
35445                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35446                 y : minY
35447             });
35448
35449             pos.push({
35450                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35451                 y : minY + (this.unitWidth + this.gutter) * 1
35452             });
35453             
35454             pos.push({
35455                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35456                 y : minY + (this.unitWidth + this.gutter) * 2
35457             });
35458             
35459             return pos;
35460             
35461         }
35462         
35463         if(box[0].size == 'xs' && box[1].size == 'xs'){
35464             
35465             pos.push({
35466                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35467                 y : minY
35468             });
35469
35470             pos.push({
35471                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35472                 y : minY
35473             });
35474             
35475             pos.push({
35476                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35477                 y : minY + (this.unitWidth + this.gutter) * 1
35478             });
35479             
35480             return pos;
35481             
35482         }
35483         
35484         pos.push({
35485             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35486             y : minY
35487         });
35488
35489         pos.push({
35490             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35491             y : minY + (this.unitWidth + this.gutter) * 2
35492         });
35493
35494         pos.push({
35495             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35496             y : minY + (this.unitWidth + this.gutter) * 2
35497         });
35498             
35499         return pos;
35500         
35501     },
35502     
35503     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35504     {
35505         var pos = [];
35506         
35507         if(box[0].size == 'xs'){
35508             
35509             pos.push({
35510                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35511                 y : minY
35512             });
35513
35514             pos.push({
35515                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35516                 y : minY
35517             });
35518             
35519             pos.push({
35520                 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),
35521                 y : minY
35522             });
35523             
35524             pos.push({
35525                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35526                 y : minY + (this.unitWidth + this.gutter) * 1
35527             });
35528             
35529             return pos;
35530             
35531         }
35532         
35533         pos.push({
35534             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35535             y : minY
35536         });
35537         
35538         pos.push({
35539             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35540             y : minY + (this.unitWidth + this.gutter) * 2
35541         });
35542         
35543         pos.push({
35544             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35545             y : minY + (this.unitWidth + this.gutter) * 2
35546         });
35547         
35548         pos.push({
35549             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),
35550             y : minY + (this.unitWidth + this.gutter) * 2
35551         });
35552
35553         return pos;
35554         
35555     },
35556     
35557     /**
35558     * remove a Masonry Brick
35559     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35560     */
35561     removeBrick : function(brick_id)
35562     {
35563         if (!brick_id) {
35564             return;
35565         }
35566         
35567         for (var i = 0; i<this.bricks.length; i++) {
35568             if (this.bricks[i].id == brick_id) {
35569                 this.bricks.splice(i,1);
35570                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35571                 this.initial();
35572             }
35573         }
35574     },
35575     
35576     /**
35577     * adds a Masonry Brick
35578     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35579     */
35580     addBrick : function(cfg)
35581     {
35582         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35583         //this.register(cn);
35584         cn.parentId = this.id;
35585         cn.render(this.el);
35586         return cn;
35587     },
35588     
35589     /**
35590     * register a Masonry Brick
35591     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35592     */
35593     
35594     register : function(brick)
35595     {
35596         this.bricks.push(brick);
35597         brick.masonryId = this.id;
35598     },
35599     
35600     /**
35601     * clear all the Masonry Brick
35602     */
35603     clearAll : function()
35604     {
35605         this.bricks = [];
35606         //this.getChildContainer().dom.innerHTML = "";
35607         this.el.dom.innerHTML = '';
35608     },
35609     
35610     getSelected : function()
35611     {
35612         if (!this.selectedBrick) {
35613             return false;
35614         }
35615         
35616         return this.selectedBrick;
35617     }
35618 });
35619
35620 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35621     
35622     groups: {},
35623      /**
35624     * register a Masonry Layout
35625     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35626     */
35627     
35628     register : function(layout)
35629     {
35630         this.groups[layout.id] = layout;
35631     },
35632     /**
35633     * fetch a  Masonry Layout based on the masonry layout ID
35634     * @param {string} the masonry layout to add
35635     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35636     */
35637     
35638     get: function(layout_id) {
35639         if (typeof(this.groups[layout_id]) == 'undefined') {
35640             return false;
35641         }
35642         return this.groups[layout_id] ;
35643     }
35644     
35645     
35646     
35647 });
35648
35649  
35650
35651  /**
35652  *
35653  * This is based on 
35654  * http://masonry.desandro.com
35655  *
35656  * The idea is to render all the bricks based on vertical width...
35657  *
35658  * The original code extends 'outlayer' - we might need to use that....
35659  * 
35660  */
35661
35662
35663 /**
35664  * @class Roo.bootstrap.LayoutMasonryAuto
35665  * @extends Roo.bootstrap.Component
35666  * Bootstrap Layout Masonry class
35667  * 
35668  * @constructor
35669  * Create a new Element
35670  * @param {Object} config The config object
35671  */
35672
35673 Roo.bootstrap.LayoutMasonryAuto = function(config){
35674     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35675 };
35676
35677 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35678     
35679       /**
35680      * @cfg {Boolean} isFitWidth  - resize the width..
35681      */   
35682     isFitWidth : false,  // options..
35683     /**
35684      * @cfg {Boolean} isOriginLeft = left align?
35685      */   
35686     isOriginLeft : true,
35687     /**
35688      * @cfg {Boolean} isOriginTop = top align?
35689      */   
35690     isOriginTop : false,
35691     /**
35692      * @cfg {Boolean} isLayoutInstant = no animation?
35693      */   
35694     isLayoutInstant : false, // needed?
35695     /**
35696      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35697      */   
35698     isResizingContainer : true,
35699     /**
35700      * @cfg {Number} columnWidth  width of the columns 
35701      */   
35702     
35703     columnWidth : 0,
35704     
35705     /**
35706      * @cfg {Number} maxCols maximum number of columns
35707      */   
35708     
35709     maxCols: 0,
35710     /**
35711      * @cfg {Number} padHeight padding below box..
35712      */   
35713     
35714     padHeight : 10, 
35715     
35716     /**
35717      * @cfg {Boolean} isAutoInitial defalut true
35718      */   
35719     
35720     isAutoInitial : true, 
35721     
35722     // private?
35723     gutter : 0,
35724     
35725     containerWidth: 0,
35726     initialColumnWidth : 0,
35727     currentSize : null,
35728     
35729     colYs : null, // array.
35730     maxY : 0,
35731     padWidth: 10,
35732     
35733     
35734     tag: 'div',
35735     cls: '',
35736     bricks: null, //CompositeElement
35737     cols : 0, // array?
35738     // element : null, // wrapped now this.el
35739     _isLayoutInited : null, 
35740     
35741     
35742     getAutoCreate : function(){
35743         
35744         var cfg = {
35745             tag: this.tag,
35746             cls: 'blog-masonary-wrapper ' + this.cls,
35747             cn : {
35748                 cls : 'mas-boxes masonary'
35749             }
35750         };
35751         
35752         return cfg;
35753     },
35754     
35755     getChildContainer: function( )
35756     {
35757         if (this.boxesEl) {
35758             return this.boxesEl;
35759         }
35760         
35761         this.boxesEl = this.el.select('.mas-boxes').first();
35762         
35763         return this.boxesEl;
35764     },
35765     
35766     
35767     initEvents : function()
35768     {
35769         var _this = this;
35770         
35771         if(this.isAutoInitial){
35772             Roo.log('hook children rendered');
35773             this.on('childrenrendered', function() {
35774                 Roo.log('children rendered');
35775                 _this.initial();
35776             } ,this);
35777         }
35778         
35779     },
35780     
35781     initial : function()
35782     {
35783         this.reloadItems();
35784
35785         this.currentSize = this.el.getBox(true);
35786
35787         /// was window resize... - let's see if this works..
35788         Roo.EventManager.onWindowResize(this.resize, this); 
35789
35790         if(!this.isAutoInitial){
35791             this.layout();
35792             return;
35793         }
35794         
35795         this.layout.defer(500,this);
35796     },
35797     
35798     reloadItems: function()
35799     {
35800         this.bricks = this.el.select('.masonry-brick', true);
35801         
35802         this.bricks.each(function(b) {
35803             //Roo.log(b.getSize());
35804             if (!b.attr('originalwidth')) {
35805                 b.attr('originalwidth',  b.getSize().width);
35806             }
35807             
35808         });
35809         
35810         Roo.log(this.bricks.elements.length);
35811     },
35812     
35813     resize : function()
35814     {
35815         Roo.log('resize');
35816         var cs = this.el.getBox(true);
35817         
35818         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35819             Roo.log("no change in with or X");
35820             return;
35821         }
35822         this.currentSize = cs;
35823         this.layout();
35824     },
35825     
35826     layout : function()
35827     {
35828          Roo.log('layout');
35829         this._resetLayout();
35830         //this._manageStamps();
35831       
35832         // don't animate first layout
35833         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35834         this.layoutItems( isInstant );
35835       
35836         // flag for initalized
35837         this._isLayoutInited = true;
35838     },
35839     
35840     layoutItems : function( isInstant )
35841     {
35842         //var items = this._getItemsForLayout( this.items );
35843         // original code supports filtering layout items.. we just ignore it..
35844         
35845         this._layoutItems( this.bricks , isInstant );
35846       
35847         this._postLayout();
35848     },
35849     _layoutItems : function ( items , isInstant)
35850     {
35851        //this.fireEvent( 'layout', this, items );
35852     
35853
35854         if ( !items || !items.elements.length ) {
35855           // no items, emit event with empty array
35856             return;
35857         }
35858
35859         var queue = [];
35860         items.each(function(item) {
35861             Roo.log("layout item");
35862             Roo.log(item);
35863             // get x/y object from method
35864             var position = this._getItemLayoutPosition( item );
35865             // enqueue
35866             position.item = item;
35867             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35868             queue.push( position );
35869         }, this);
35870       
35871         this._processLayoutQueue( queue );
35872     },
35873     /** Sets position of item in DOM
35874     * @param {Element} item
35875     * @param {Number} x - horizontal position
35876     * @param {Number} y - vertical position
35877     * @param {Boolean} isInstant - disables transitions
35878     */
35879     _processLayoutQueue : function( queue )
35880     {
35881         for ( var i=0, len = queue.length; i < len; i++ ) {
35882             var obj = queue[i];
35883             obj.item.position('absolute');
35884             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35885         }
35886     },
35887       
35888     
35889     /**
35890     * Any logic you want to do after each layout,
35891     * i.e. size the container
35892     */
35893     _postLayout : function()
35894     {
35895         this.resizeContainer();
35896     },
35897     
35898     resizeContainer : function()
35899     {
35900         if ( !this.isResizingContainer ) {
35901             return;
35902         }
35903         var size = this._getContainerSize();
35904         if ( size ) {
35905             this.el.setSize(size.width,size.height);
35906             this.boxesEl.setSize(size.width,size.height);
35907         }
35908     },
35909     
35910     
35911     
35912     _resetLayout : function()
35913     {
35914         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35915         this.colWidth = this.el.getWidth();
35916         //this.gutter = this.el.getWidth(); 
35917         
35918         this.measureColumns();
35919
35920         // reset column Y
35921         var i = this.cols;
35922         this.colYs = [];
35923         while (i--) {
35924             this.colYs.push( 0 );
35925         }
35926     
35927         this.maxY = 0;
35928     },
35929
35930     measureColumns : function()
35931     {
35932         this.getContainerWidth();
35933       // if columnWidth is 0, default to outerWidth of first item
35934         if ( !this.columnWidth ) {
35935             var firstItem = this.bricks.first();
35936             Roo.log(firstItem);
35937             this.columnWidth  = this.containerWidth;
35938             if (firstItem && firstItem.attr('originalwidth') ) {
35939                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35940             }
35941             // columnWidth fall back to item of first element
35942             Roo.log("set column width?");
35943                         this.initialColumnWidth = this.columnWidth  ;
35944
35945             // if first elem has no width, default to size of container
35946             
35947         }
35948         
35949         
35950         if (this.initialColumnWidth) {
35951             this.columnWidth = this.initialColumnWidth;
35952         }
35953         
35954         
35955             
35956         // column width is fixed at the top - however if container width get's smaller we should
35957         // reduce it...
35958         
35959         // this bit calcs how man columns..
35960             
35961         var columnWidth = this.columnWidth += this.gutter;
35962       
35963         // calculate columns
35964         var containerWidth = this.containerWidth + this.gutter;
35965         
35966         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35967         // fix rounding errors, typically with gutters
35968         var excess = columnWidth - containerWidth % columnWidth;
35969         
35970         
35971         // if overshoot is less than a pixel, round up, otherwise floor it
35972         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35973         cols = Math[ mathMethod ]( cols );
35974         this.cols = Math.max( cols, 1 );
35975         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35976         
35977          // padding positioning..
35978         var totalColWidth = this.cols * this.columnWidth;
35979         var padavail = this.containerWidth - totalColWidth;
35980         // so for 2 columns - we need 3 'pads'
35981         
35982         var padNeeded = (1+this.cols) * this.padWidth;
35983         
35984         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35985         
35986         this.columnWidth += padExtra
35987         //this.padWidth = Math.floor(padavail /  ( this.cols));
35988         
35989         // adjust colum width so that padding is fixed??
35990         
35991         // we have 3 columns ... total = width * 3
35992         // we have X left over... that should be used by 
35993         
35994         //if (this.expandC) {
35995             
35996         //}
35997         
35998         
35999         
36000     },
36001     
36002     getContainerWidth : function()
36003     {
36004        /* // container is parent if fit width
36005         var container = this.isFitWidth ? this.element.parentNode : this.element;
36006         // check that this.size and size are there
36007         // IE8 triggers resize on body size change, so they might not be
36008         
36009         var size = getSize( container );  //FIXME
36010         this.containerWidth = size && size.innerWidth; //FIXME
36011         */
36012          
36013         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36014         
36015     },
36016     
36017     _getItemLayoutPosition : function( item )  // what is item?
36018     {
36019         // we resize the item to our columnWidth..
36020       
36021         item.setWidth(this.columnWidth);
36022         item.autoBoxAdjust  = false;
36023         
36024         var sz = item.getSize();
36025  
36026         // how many columns does this brick span
36027         var remainder = this.containerWidth % this.columnWidth;
36028         
36029         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36030         // round if off by 1 pixel, otherwise use ceil
36031         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36032         colSpan = Math.min( colSpan, this.cols );
36033         
36034         // normally this should be '1' as we dont' currently allow multi width columns..
36035         
36036         var colGroup = this._getColGroup( colSpan );
36037         // get the minimum Y value from the columns
36038         var minimumY = Math.min.apply( Math, colGroup );
36039         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36040         
36041         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36042          
36043         // position the brick
36044         var position = {
36045             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36046             y: this.currentSize.y + minimumY + this.padHeight
36047         };
36048         
36049         Roo.log(position);
36050         // apply setHeight to necessary columns
36051         var setHeight = minimumY + sz.height + this.padHeight;
36052         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36053         
36054         var setSpan = this.cols + 1 - colGroup.length;
36055         for ( var i = 0; i < setSpan; i++ ) {
36056           this.colYs[ shortColIndex + i ] = setHeight ;
36057         }
36058       
36059         return position;
36060     },
36061     
36062     /**
36063      * @param {Number} colSpan - number of columns the element spans
36064      * @returns {Array} colGroup
36065      */
36066     _getColGroup : function( colSpan )
36067     {
36068         if ( colSpan < 2 ) {
36069           // if brick spans only one column, use all the column Ys
36070           return this.colYs;
36071         }
36072       
36073         var colGroup = [];
36074         // how many different places could this brick fit horizontally
36075         var groupCount = this.cols + 1 - colSpan;
36076         // for each group potential horizontal position
36077         for ( var i = 0; i < groupCount; i++ ) {
36078           // make an array of colY values for that one group
36079           var groupColYs = this.colYs.slice( i, i + colSpan );
36080           // and get the max value of the array
36081           colGroup[i] = Math.max.apply( Math, groupColYs );
36082         }
36083         return colGroup;
36084     },
36085     /*
36086     _manageStamp : function( stamp )
36087     {
36088         var stampSize =  stamp.getSize();
36089         var offset = stamp.getBox();
36090         // get the columns that this stamp affects
36091         var firstX = this.isOriginLeft ? offset.x : offset.right;
36092         var lastX = firstX + stampSize.width;
36093         var firstCol = Math.floor( firstX / this.columnWidth );
36094         firstCol = Math.max( 0, firstCol );
36095         
36096         var lastCol = Math.floor( lastX / this.columnWidth );
36097         // lastCol should not go over if multiple of columnWidth #425
36098         lastCol -= lastX % this.columnWidth ? 0 : 1;
36099         lastCol = Math.min( this.cols - 1, lastCol );
36100         
36101         // set colYs to bottom of the stamp
36102         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36103             stampSize.height;
36104             
36105         for ( var i = firstCol; i <= lastCol; i++ ) {
36106           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36107         }
36108     },
36109     */
36110     
36111     _getContainerSize : function()
36112     {
36113         this.maxY = Math.max.apply( Math, this.colYs );
36114         var size = {
36115             height: this.maxY
36116         };
36117       
36118         if ( this.isFitWidth ) {
36119             size.width = this._getContainerFitWidth();
36120         }
36121       
36122         return size;
36123     },
36124     
36125     _getContainerFitWidth : function()
36126     {
36127         var unusedCols = 0;
36128         // count unused columns
36129         var i = this.cols;
36130         while ( --i ) {
36131           if ( this.colYs[i] !== 0 ) {
36132             break;
36133           }
36134           unusedCols++;
36135         }
36136         // fit container to columns that have been used
36137         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36138     },
36139     
36140     needsResizeLayout : function()
36141     {
36142         var previousWidth = this.containerWidth;
36143         this.getContainerWidth();
36144         return previousWidth !== this.containerWidth;
36145     }
36146  
36147 });
36148
36149  
36150
36151  /*
36152  * - LGPL
36153  *
36154  * element
36155  * 
36156  */
36157
36158 /**
36159  * @class Roo.bootstrap.MasonryBrick
36160  * @extends Roo.bootstrap.Component
36161  * Bootstrap MasonryBrick class
36162  * 
36163  * @constructor
36164  * Create a new MasonryBrick
36165  * @param {Object} config The config object
36166  */
36167
36168 Roo.bootstrap.MasonryBrick = function(config){
36169     
36170     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36171     
36172     Roo.bootstrap.MasonryBrick.register(this);
36173     
36174     this.addEvents({
36175         // raw events
36176         /**
36177          * @event click
36178          * When a MasonryBrick is clcik
36179          * @param {Roo.bootstrap.MasonryBrick} this
36180          * @param {Roo.EventObject} e
36181          */
36182         "click" : true
36183     });
36184 };
36185
36186 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36187     
36188     /**
36189      * @cfg {String} title
36190      */   
36191     title : '',
36192     /**
36193      * @cfg {String} html
36194      */   
36195     html : '',
36196     /**
36197      * @cfg {String} bgimage
36198      */   
36199     bgimage : '',
36200     /**
36201      * @cfg {String} videourl
36202      */   
36203     videourl : '',
36204     /**
36205      * @cfg {String} cls
36206      */   
36207     cls : '',
36208     /**
36209      * @cfg {String} href
36210      */   
36211     href : '',
36212     /**
36213      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36214      */   
36215     size : 'xs',
36216     
36217     /**
36218      * @cfg {String} placetitle (center|bottom)
36219      */   
36220     placetitle : '',
36221     
36222     /**
36223      * @cfg {Boolean} isFitContainer defalut true
36224      */   
36225     isFitContainer : true, 
36226     
36227     /**
36228      * @cfg {Boolean} preventDefault defalut false
36229      */   
36230     preventDefault : false, 
36231     
36232     /**
36233      * @cfg {Boolean} inverse defalut false
36234      */   
36235     maskInverse : false, 
36236     
36237     getAutoCreate : function()
36238     {
36239         if(!this.isFitContainer){
36240             return this.getSplitAutoCreate();
36241         }
36242         
36243         var cls = 'masonry-brick masonry-brick-full';
36244         
36245         if(this.href.length){
36246             cls += ' masonry-brick-link';
36247         }
36248         
36249         if(this.bgimage.length){
36250             cls += ' masonry-brick-image';
36251         }
36252         
36253         if(this.maskInverse){
36254             cls += ' mask-inverse';
36255         }
36256         
36257         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36258             cls += ' enable-mask';
36259         }
36260         
36261         if(this.size){
36262             cls += ' masonry-' + this.size + '-brick';
36263         }
36264         
36265         if(this.placetitle.length){
36266             
36267             switch (this.placetitle) {
36268                 case 'center' :
36269                     cls += ' masonry-center-title';
36270                     break;
36271                 case 'bottom' :
36272                     cls += ' masonry-bottom-title';
36273                     break;
36274                 default:
36275                     break;
36276             }
36277             
36278         } else {
36279             if(!this.html.length && !this.bgimage.length){
36280                 cls += ' masonry-center-title';
36281             }
36282
36283             if(!this.html.length && this.bgimage.length){
36284                 cls += ' masonry-bottom-title';
36285             }
36286         }
36287         
36288         if(this.cls){
36289             cls += ' ' + this.cls;
36290         }
36291         
36292         var cfg = {
36293             tag: (this.href.length) ? 'a' : 'div',
36294             cls: cls,
36295             cn: [
36296                 {
36297                     tag: 'div',
36298                     cls: 'masonry-brick-mask'
36299                 },
36300                 {
36301                     tag: 'div',
36302                     cls: 'masonry-brick-paragraph',
36303                     cn: []
36304                 }
36305             ]
36306         };
36307         
36308         if(this.href.length){
36309             cfg.href = this.href;
36310         }
36311         
36312         var cn = cfg.cn[1].cn;
36313         
36314         if(this.title.length){
36315             cn.push({
36316                 tag: 'h4',
36317                 cls: 'masonry-brick-title',
36318                 html: this.title
36319             });
36320         }
36321         
36322         if(this.html.length){
36323             cn.push({
36324                 tag: 'p',
36325                 cls: 'masonry-brick-text',
36326                 html: this.html
36327             });
36328         }
36329         
36330         if (!this.title.length && !this.html.length) {
36331             cfg.cn[1].cls += ' hide';
36332         }
36333         
36334         if(this.bgimage.length){
36335             cfg.cn.push({
36336                 tag: 'img',
36337                 cls: 'masonry-brick-image-view',
36338                 src: this.bgimage
36339             });
36340         }
36341         
36342         if(this.videourl.length){
36343             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36344             // youtube support only?
36345             cfg.cn.push({
36346                 tag: 'iframe',
36347                 cls: 'masonry-brick-image-view',
36348                 src: vurl,
36349                 frameborder : 0,
36350                 allowfullscreen : true
36351             });
36352         }
36353         
36354         return cfg;
36355         
36356     },
36357     
36358     getSplitAutoCreate : function()
36359     {
36360         var cls = 'masonry-brick masonry-brick-split';
36361         
36362         if(this.href.length){
36363             cls += ' masonry-brick-link';
36364         }
36365         
36366         if(this.bgimage.length){
36367             cls += ' masonry-brick-image';
36368         }
36369         
36370         if(this.size){
36371             cls += ' masonry-' + this.size + '-brick';
36372         }
36373         
36374         switch (this.placetitle) {
36375             case 'center' :
36376                 cls += ' masonry-center-title';
36377                 break;
36378             case 'bottom' :
36379                 cls += ' masonry-bottom-title';
36380                 break;
36381             default:
36382                 if(!this.bgimage.length){
36383                     cls += ' masonry-center-title';
36384                 }
36385
36386                 if(this.bgimage.length){
36387                     cls += ' masonry-bottom-title';
36388                 }
36389                 break;
36390         }
36391         
36392         if(this.cls){
36393             cls += ' ' + this.cls;
36394         }
36395         
36396         var cfg = {
36397             tag: (this.href.length) ? 'a' : 'div',
36398             cls: cls,
36399             cn: [
36400                 {
36401                     tag: 'div',
36402                     cls: 'masonry-brick-split-head',
36403                     cn: [
36404                         {
36405                             tag: 'div',
36406                             cls: 'masonry-brick-paragraph',
36407                             cn: []
36408                         }
36409                     ]
36410                 },
36411                 {
36412                     tag: 'div',
36413                     cls: 'masonry-brick-split-body',
36414                     cn: []
36415                 }
36416             ]
36417         };
36418         
36419         if(this.href.length){
36420             cfg.href = this.href;
36421         }
36422         
36423         if(this.title.length){
36424             cfg.cn[0].cn[0].cn.push({
36425                 tag: 'h4',
36426                 cls: 'masonry-brick-title',
36427                 html: this.title
36428             });
36429         }
36430         
36431         if(this.html.length){
36432             cfg.cn[1].cn.push({
36433                 tag: 'p',
36434                 cls: 'masonry-brick-text',
36435                 html: this.html
36436             });
36437         }
36438
36439         if(this.bgimage.length){
36440             cfg.cn[0].cn.push({
36441                 tag: 'img',
36442                 cls: 'masonry-brick-image-view',
36443                 src: this.bgimage
36444             });
36445         }
36446         
36447         if(this.videourl.length){
36448             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36449             // youtube support only?
36450             cfg.cn[0].cn.cn.push({
36451                 tag: 'iframe',
36452                 cls: 'masonry-brick-image-view',
36453                 src: vurl,
36454                 frameborder : 0,
36455                 allowfullscreen : true
36456             });
36457         }
36458         
36459         return cfg;
36460     },
36461     
36462     initEvents: function() 
36463     {
36464         switch (this.size) {
36465             case 'xs' :
36466                 this.x = 1;
36467                 this.y = 1;
36468                 break;
36469             case 'sm' :
36470                 this.x = 2;
36471                 this.y = 2;
36472                 break;
36473             case 'md' :
36474             case 'md-left' :
36475             case 'md-right' :
36476                 this.x = 3;
36477                 this.y = 3;
36478                 break;
36479             case 'tall' :
36480                 this.x = 2;
36481                 this.y = 3;
36482                 break;
36483             case 'wide' :
36484                 this.x = 3;
36485                 this.y = 2;
36486                 break;
36487             case 'wide-thin' :
36488                 this.x = 3;
36489                 this.y = 1;
36490                 break;
36491                         
36492             default :
36493                 break;
36494         }
36495         
36496         if(Roo.isTouch){
36497             this.el.on('touchstart', this.onTouchStart, this);
36498             this.el.on('touchmove', this.onTouchMove, this);
36499             this.el.on('touchend', this.onTouchEnd, this);
36500             this.el.on('contextmenu', this.onContextMenu, this);
36501         } else {
36502             this.el.on('mouseenter'  ,this.enter, this);
36503             this.el.on('mouseleave', this.leave, this);
36504             this.el.on('click', this.onClick, this);
36505         }
36506         
36507         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36508             this.parent().bricks.push(this);   
36509         }
36510         
36511     },
36512     
36513     onClick: function(e, el)
36514     {
36515         var time = this.endTimer - this.startTimer;
36516         // Roo.log(e.preventDefault());
36517         if(Roo.isTouch){
36518             if(time > 1000){
36519                 e.preventDefault();
36520                 return;
36521             }
36522         }
36523         
36524         if(!this.preventDefault){
36525             return;
36526         }
36527         
36528         e.preventDefault();
36529         
36530         if (this.activeClass != '') {
36531             this.selectBrick();
36532         }
36533         
36534         this.fireEvent('click', this, e);
36535     },
36536     
36537     enter: function(e, el)
36538     {
36539         e.preventDefault();
36540         
36541         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36542             return;
36543         }
36544         
36545         if(this.bgimage.length && this.html.length){
36546             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36547         }
36548     },
36549     
36550     leave: function(e, el)
36551     {
36552         e.preventDefault();
36553         
36554         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36555             return;
36556         }
36557         
36558         if(this.bgimage.length && this.html.length){
36559             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36560         }
36561     },
36562     
36563     onTouchStart: function(e, el)
36564     {
36565 //        e.preventDefault();
36566         
36567         this.touchmoved = false;
36568         
36569         if(!this.isFitContainer){
36570             return;
36571         }
36572         
36573         if(!this.bgimage.length || !this.html.length){
36574             return;
36575         }
36576         
36577         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36578         
36579         this.timer = new Date().getTime();
36580         
36581     },
36582     
36583     onTouchMove: function(e, el)
36584     {
36585         this.touchmoved = true;
36586     },
36587     
36588     onContextMenu : function(e,el)
36589     {
36590         e.preventDefault();
36591         e.stopPropagation();
36592         return false;
36593     },
36594     
36595     onTouchEnd: function(e, el)
36596     {
36597 //        e.preventDefault();
36598         
36599         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36600         
36601             this.leave(e,el);
36602             
36603             return;
36604         }
36605         
36606         if(!this.bgimage.length || !this.html.length){
36607             
36608             if(this.href.length){
36609                 window.location.href = this.href;
36610             }
36611             
36612             return;
36613         }
36614         
36615         if(!this.isFitContainer){
36616             return;
36617         }
36618         
36619         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36620         
36621         window.location.href = this.href;
36622     },
36623     
36624     //selection on single brick only
36625     selectBrick : function() {
36626         
36627         if (!this.parentId) {
36628             return;
36629         }
36630         
36631         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36632         var index = m.selectedBrick.indexOf(this.id);
36633         
36634         if ( index > -1) {
36635             m.selectedBrick.splice(index,1);
36636             this.el.removeClass(this.activeClass);
36637             return;
36638         }
36639         
36640         for(var i = 0; i < m.selectedBrick.length; i++) {
36641             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36642             b.el.removeClass(b.activeClass);
36643         }
36644         
36645         m.selectedBrick = [];
36646         
36647         m.selectedBrick.push(this.id);
36648         this.el.addClass(this.activeClass);
36649         return;
36650     },
36651     
36652     isSelected : function(){
36653         return this.el.hasClass(this.activeClass);
36654         
36655     }
36656 });
36657
36658 Roo.apply(Roo.bootstrap.MasonryBrick, {
36659     
36660     //groups: {},
36661     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36662      /**
36663     * register a Masonry Brick
36664     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36665     */
36666     
36667     register : function(brick)
36668     {
36669         //this.groups[brick.id] = brick;
36670         this.groups.add(brick.id, brick);
36671     },
36672     /**
36673     * fetch a  masonry brick based on the masonry brick ID
36674     * @param {string} the masonry brick to add
36675     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36676     */
36677     
36678     get: function(brick_id) 
36679     {
36680         // if (typeof(this.groups[brick_id]) == 'undefined') {
36681         //     return false;
36682         // }
36683         // return this.groups[brick_id] ;
36684         
36685         if(this.groups.key(brick_id)) {
36686             return this.groups.key(brick_id);
36687         }
36688         
36689         return false;
36690     }
36691     
36692     
36693     
36694 });
36695
36696  /*
36697  * - LGPL
36698  *
36699  * element
36700  * 
36701  */
36702
36703 /**
36704  * @class Roo.bootstrap.Brick
36705  * @extends Roo.bootstrap.Component
36706  * Bootstrap Brick class
36707  * 
36708  * @constructor
36709  * Create a new Brick
36710  * @param {Object} config The config object
36711  */
36712
36713 Roo.bootstrap.Brick = function(config){
36714     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36715     
36716     this.addEvents({
36717         // raw events
36718         /**
36719          * @event click
36720          * When a Brick is click
36721          * @param {Roo.bootstrap.Brick} this
36722          * @param {Roo.EventObject} e
36723          */
36724         "click" : true
36725     });
36726 };
36727
36728 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36729     
36730     /**
36731      * @cfg {String} title
36732      */   
36733     title : '',
36734     /**
36735      * @cfg {String} html
36736      */   
36737     html : '',
36738     /**
36739      * @cfg {String} bgimage
36740      */   
36741     bgimage : '',
36742     /**
36743      * @cfg {String} cls
36744      */   
36745     cls : '',
36746     /**
36747      * @cfg {String} href
36748      */   
36749     href : '',
36750     /**
36751      * @cfg {String} video
36752      */   
36753     video : '',
36754     /**
36755      * @cfg {Boolean} square
36756      */   
36757     square : true,
36758     
36759     getAutoCreate : function()
36760     {
36761         var cls = 'roo-brick';
36762         
36763         if(this.href.length){
36764             cls += ' roo-brick-link';
36765         }
36766         
36767         if(this.bgimage.length){
36768             cls += ' roo-brick-image';
36769         }
36770         
36771         if(!this.html.length && !this.bgimage.length){
36772             cls += ' roo-brick-center-title';
36773         }
36774         
36775         if(!this.html.length && this.bgimage.length){
36776             cls += ' roo-brick-bottom-title';
36777         }
36778         
36779         if(this.cls){
36780             cls += ' ' + this.cls;
36781         }
36782         
36783         var cfg = {
36784             tag: (this.href.length) ? 'a' : 'div',
36785             cls: cls,
36786             cn: [
36787                 {
36788                     tag: 'div',
36789                     cls: 'roo-brick-paragraph',
36790                     cn: []
36791                 }
36792             ]
36793         };
36794         
36795         if(this.href.length){
36796             cfg.href = this.href;
36797         }
36798         
36799         var cn = cfg.cn[0].cn;
36800         
36801         if(this.title.length){
36802             cn.push({
36803                 tag: 'h4',
36804                 cls: 'roo-brick-title',
36805                 html: this.title
36806             });
36807         }
36808         
36809         if(this.html.length){
36810             cn.push({
36811                 tag: 'p',
36812                 cls: 'roo-brick-text',
36813                 html: this.html
36814             });
36815         } else {
36816             cn.cls += ' hide';
36817         }
36818         
36819         if(this.bgimage.length){
36820             cfg.cn.push({
36821                 tag: 'img',
36822                 cls: 'roo-brick-image-view',
36823                 src: this.bgimage
36824             });
36825         }
36826         
36827         return cfg;
36828     },
36829     
36830     initEvents: function() 
36831     {
36832         if(this.title.length || this.html.length){
36833             this.el.on('mouseenter'  ,this.enter, this);
36834             this.el.on('mouseleave', this.leave, this);
36835         }
36836         
36837         Roo.EventManager.onWindowResize(this.resize, this); 
36838         
36839         if(this.bgimage.length){
36840             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36841             this.imageEl.on('load', this.onImageLoad, this);
36842             return;
36843         }
36844         
36845         this.resize();
36846     },
36847     
36848     onImageLoad : function()
36849     {
36850         this.resize();
36851     },
36852     
36853     resize : function()
36854     {
36855         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36856         
36857         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36858         
36859         if(this.bgimage.length){
36860             var image = this.el.select('.roo-brick-image-view', true).first();
36861             
36862             image.setWidth(paragraph.getWidth());
36863             
36864             if(this.square){
36865                 image.setHeight(paragraph.getWidth());
36866             }
36867             
36868             this.el.setHeight(image.getHeight());
36869             paragraph.setHeight(image.getHeight());
36870             
36871         }
36872         
36873     },
36874     
36875     enter: function(e, el)
36876     {
36877         e.preventDefault();
36878         
36879         if(this.bgimage.length){
36880             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36881             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36882         }
36883     },
36884     
36885     leave: function(e, el)
36886     {
36887         e.preventDefault();
36888         
36889         if(this.bgimage.length){
36890             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36891             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36892         }
36893     }
36894     
36895 });
36896
36897  
36898
36899  /*
36900  * - LGPL
36901  *
36902  * Number field 
36903  */
36904
36905 /**
36906  * @class Roo.bootstrap.NumberField
36907  * @extends Roo.bootstrap.Input
36908  * Bootstrap NumberField class
36909  * 
36910  * 
36911  * 
36912  * 
36913  * @constructor
36914  * Create a new NumberField
36915  * @param {Object} config The config object
36916  */
36917
36918 Roo.bootstrap.NumberField = function(config){
36919     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36920 };
36921
36922 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36923     
36924     /**
36925      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36926      */
36927     allowDecimals : true,
36928     /**
36929      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36930      */
36931     decimalSeparator : ".",
36932     /**
36933      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36934      */
36935     decimalPrecision : 2,
36936     /**
36937      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36938      */
36939     allowNegative : true,
36940     
36941     /**
36942      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36943      */
36944     allowZero: true,
36945     /**
36946      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36947      */
36948     minValue : Number.NEGATIVE_INFINITY,
36949     /**
36950      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36951      */
36952     maxValue : Number.MAX_VALUE,
36953     /**
36954      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36955      */
36956     minText : "The minimum value for this field is {0}",
36957     /**
36958      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36959      */
36960     maxText : "The maximum value for this field is {0}",
36961     /**
36962      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36963      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36964      */
36965     nanText : "{0} is not a valid number",
36966     /**
36967      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36968      */
36969     thousandsDelimiter : false,
36970     /**
36971      * @cfg {String} valueAlign alignment of value
36972      */
36973     valueAlign : "left",
36974
36975     getAutoCreate : function()
36976     {
36977         var hiddenInput = {
36978             tag: 'input',
36979             type: 'hidden',
36980             id: Roo.id(),
36981             cls: 'hidden-number-input'
36982         };
36983         
36984         if (this.name) {
36985             hiddenInput.name = this.name;
36986         }
36987         
36988         this.name = '';
36989         
36990         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36991         
36992         this.name = hiddenInput.name;
36993         
36994         if(cfg.cn.length > 0) {
36995             cfg.cn.push(hiddenInput);
36996         }
36997         
36998         return cfg;
36999     },
37000
37001     // private
37002     initEvents : function()
37003     {   
37004         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37005         
37006         var allowed = "0123456789";
37007         
37008         if(this.allowDecimals){
37009             allowed += this.decimalSeparator;
37010         }
37011         
37012         if(this.allowNegative){
37013             allowed += "-";
37014         }
37015         
37016         if(this.thousandsDelimiter) {
37017             allowed += ",";
37018         }
37019         
37020         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37021         
37022         var keyPress = function(e){
37023             
37024             var k = e.getKey();
37025             
37026             var c = e.getCharCode();
37027             
37028             if(
37029                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37030                     allowed.indexOf(String.fromCharCode(c)) === -1
37031             ){
37032                 e.stopEvent();
37033                 return;
37034             }
37035             
37036             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37037                 return;
37038             }
37039             
37040             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37041                 e.stopEvent();
37042             }
37043         };
37044         
37045         this.el.on("keypress", keyPress, this);
37046     },
37047     
37048     validateValue : function(value)
37049     {
37050         
37051         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37052             return false;
37053         }
37054         
37055         var num = this.parseValue(value);
37056         
37057         if(isNaN(num)){
37058             this.markInvalid(String.format(this.nanText, value));
37059             return false;
37060         }
37061         
37062         if(num < this.minValue){
37063             this.markInvalid(String.format(this.minText, this.minValue));
37064             return false;
37065         }
37066         
37067         if(num > this.maxValue){
37068             this.markInvalid(String.format(this.maxText, this.maxValue));
37069             return false;
37070         }
37071         
37072         return true;
37073     },
37074
37075     getValue : function()
37076     {
37077         var v = this.hiddenEl().getValue();
37078         
37079         return this.fixPrecision(this.parseValue(v));
37080     },
37081
37082     parseValue : function(value)
37083     {
37084         if(this.thousandsDelimiter) {
37085             value += "";
37086             r = new RegExp(",", "g");
37087             value = value.replace(r, "");
37088         }
37089         
37090         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37091         return isNaN(value) ? '' : value;
37092     },
37093
37094     fixPrecision : function(value)
37095     {
37096         if(this.thousandsDelimiter) {
37097             value += "";
37098             r = new RegExp(",", "g");
37099             value = value.replace(r, "");
37100         }
37101         
37102         var nan = isNaN(value);
37103         
37104         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37105             return nan ? '' : value;
37106         }
37107         return parseFloat(value).toFixed(this.decimalPrecision);
37108     },
37109
37110     setValue : function(v)
37111     {
37112         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37113         
37114         this.value = v;
37115         
37116         if(this.rendered){
37117             
37118             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37119             
37120             this.inputEl().dom.value = (v == '') ? '' :
37121                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37122             
37123             if(!this.allowZero && v === '0') {
37124                 this.hiddenEl().dom.value = '';
37125                 this.inputEl().dom.value = '';
37126             }
37127             
37128             this.validate();
37129         }
37130     },
37131
37132     decimalPrecisionFcn : function(v)
37133     {
37134         return Math.floor(v);
37135     },
37136
37137     beforeBlur : function()
37138     {
37139         var v = this.parseValue(this.getRawValue());
37140         
37141         if(v || v === 0 || v === ''){
37142             this.setValue(v);
37143         }
37144     },
37145     
37146     hiddenEl : function()
37147     {
37148         return this.el.select('input.hidden-number-input',true).first();
37149     }
37150     
37151 });
37152
37153  
37154
37155 /*
37156 * Licence: LGPL
37157 */
37158
37159 /**
37160  * @class Roo.bootstrap.DocumentSlider
37161  * @extends Roo.bootstrap.Component
37162  * Bootstrap DocumentSlider class
37163  * 
37164  * @constructor
37165  * Create a new DocumentViewer
37166  * @param {Object} config The config object
37167  */
37168
37169 Roo.bootstrap.DocumentSlider = function(config){
37170     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37171     
37172     this.files = [];
37173     
37174     this.addEvents({
37175         /**
37176          * @event initial
37177          * Fire after initEvent
37178          * @param {Roo.bootstrap.DocumentSlider} this
37179          */
37180         "initial" : true,
37181         /**
37182          * @event update
37183          * Fire after update
37184          * @param {Roo.bootstrap.DocumentSlider} this
37185          */
37186         "update" : true,
37187         /**
37188          * @event click
37189          * Fire after click
37190          * @param {Roo.bootstrap.DocumentSlider} this
37191          */
37192         "click" : true
37193     });
37194 };
37195
37196 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37197     
37198     files : false,
37199     
37200     indicator : 0,
37201     
37202     getAutoCreate : function()
37203     {
37204         var cfg = {
37205             tag : 'div',
37206             cls : 'roo-document-slider',
37207             cn : [
37208                 {
37209                     tag : 'div',
37210                     cls : 'roo-document-slider-header',
37211                     cn : [
37212                         {
37213                             tag : 'div',
37214                             cls : 'roo-document-slider-header-title'
37215                         }
37216                     ]
37217                 },
37218                 {
37219                     tag : 'div',
37220                     cls : 'roo-document-slider-body',
37221                     cn : [
37222                         {
37223                             tag : 'div',
37224                             cls : 'roo-document-slider-prev',
37225                             cn : [
37226                                 {
37227                                     tag : 'i',
37228                                     cls : 'fa fa-chevron-left'
37229                                 }
37230                             ]
37231                         },
37232                         {
37233                             tag : 'div',
37234                             cls : 'roo-document-slider-thumb',
37235                             cn : [
37236                                 {
37237                                     tag : 'img',
37238                                     cls : 'roo-document-slider-image'
37239                                 }
37240                             ]
37241                         },
37242                         {
37243                             tag : 'div',
37244                             cls : 'roo-document-slider-next',
37245                             cn : [
37246                                 {
37247                                     tag : 'i',
37248                                     cls : 'fa fa-chevron-right'
37249                                 }
37250                             ]
37251                         }
37252                     ]
37253                 }
37254             ]
37255         };
37256         
37257         return cfg;
37258     },
37259     
37260     initEvents : function()
37261     {
37262         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37263         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37264         
37265         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37266         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37267         
37268         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37269         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37270         
37271         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37272         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37273         
37274         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37275         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37276         
37277         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37278         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37279         
37280         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37281         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37282         
37283         this.thumbEl.on('click', this.onClick, this);
37284         
37285         this.prevIndicator.on('click', this.prev, this);
37286         
37287         this.nextIndicator.on('click', this.next, this);
37288         
37289     },
37290     
37291     initial : function()
37292     {
37293         if(this.files.length){
37294             this.indicator = 1;
37295             this.update()
37296         }
37297         
37298         this.fireEvent('initial', this);
37299     },
37300     
37301     update : function()
37302     {
37303         this.imageEl.attr('src', this.files[this.indicator - 1]);
37304         
37305         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37306         
37307         this.prevIndicator.show();
37308         
37309         if(this.indicator == 1){
37310             this.prevIndicator.hide();
37311         }
37312         
37313         this.nextIndicator.show();
37314         
37315         if(this.indicator == this.files.length){
37316             this.nextIndicator.hide();
37317         }
37318         
37319         this.thumbEl.scrollTo('top');
37320         
37321         this.fireEvent('update', this);
37322     },
37323     
37324     onClick : function(e)
37325     {
37326         e.preventDefault();
37327         
37328         this.fireEvent('click', this);
37329     },
37330     
37331     prev : function(e)
37332     {
37333         e.preventDefault();
37334         
37335         this.indicator = Math.max(1, this.indicator - 1);
37336         
37337         this.update();
37338     },
37339     
37340     next : function(e)
37341     {
37342         e.preventDefault();
37343         
37344         this.indicator = Math.min(this.files.length, this.indicator + 1);
37345         
37346         this.update();
37347     }
37348 });
37349 /*
37350  * - LGPL
37351  *
37352  * RadioSet
37353  *
37354  *
37355  */
37356
37357 /**
37358  * @class Roo.bootstrap.RadioSet
37359  * @extends Roo.bootstrap.Input
37360  * Bootstrap RadioSet class
37361  * @cfg {String} indicatorpos (left|right) default left
37362  * @cfg {Boolean} inline (true|false) inline the element (default true)
37363  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37364  * @constructor
37365  * Create a new RadioSet
37366  * @param {Object} config The config object
37367  */
37368
37369 Roo.bootstrap.RadioSet = function(config){
37370     
37371     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37372     
37373     this.radioes = [];
37374     
37375     Roo.bootstrap.RadioSet.register(this);
37376     
37377     this.addEvents({
37378         /**
37379         * @event check
37380         * Fires when the element is checked or unchecked.
37381         * @param {Roo.bootstrap.RadioSet} this This radio
37382         * @param {Roo.bootstrap.Radio} item The checked item
37383         */
37384        check : true,
37385        /**
37386         * @event click
37387         * Fires when the element is click.
37388         * @param {Roo.bootstrap.RadioSet} this This radio set
37389         * @param {Roo.bootstrap.Radio} item The checked item
37390         * @param {Roo.EventObject} e The event object
37391         */
37392        click : true
37393     });
37394     
37395 };
37396
37397 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37398
37399     radioes : false,
37400     
37401     inline : true,
37402     
37403     weight : '',
37404     
37405     indicatorpos : 'left',
37406     
37407     getAutoCreate : function()
37408     {
37409         var label = {
37410             tag : 'label',
37411             cls : 'roo-radio-set-label',
37412             cn : [
37413                 {
37414                     tag : 'span',
37415                     html : this.fieldLabel
37416                 }
37417             ]
37418         };
37419         if (Roo.bootstrap.version == 3) {
37420             
37421             
37422             if(this.indicatorpos == 'left'){
37423                 label.cn.unshift({
37424                     tag : 'i',
37425                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37426                     tooltip : 'This field is required'
37427                 });
37428             } else {
37429                 label.cn.push({
37430                     tag : 'i',
37431                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37432                     tooltip : 'This field is required'
37433                 });
37434             }
37435         }
37436         var items = {
37437             tag : 'div',
37438             cls : 'roo-radio-set-items'
37439         };
37440         
37441         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37442         
37443         if (align === 'left' && this.fieldLabel.length) {
37444             
37445             items = {
37446                 cls : "roo-radio-set-right", 
37447                 cn: [
37448                     items
37449                 ]
37450             };
37451             
37452             if(this.labelWidth > 12){
37453                 label.style = "width: " + this.labelWidth + 'px';
37454             }
37455             
37456             if(this.labelWidth < 13 && this.labelmd == 0){
37457                 this.labelmd = this.labelWidth;
37458             }
37459             
37460             if(this.labellg > 0){
37461                 label.cls += ' col-lg-' + this.labellg;
37462                 items.cls += ' col-lg-' + (12 - this.labellg);
37463             }
37464             
37465             if(this.labelmd > 0){
37466                 label.cls += ' col-md-' + this.labelmd;
37467                 items.cls += ' col-md-' + (12 - this.labelmd);
37468             }
37469             
37470             if(this.labelsm > 0){
37471                 label.cls += ' col-sm-' + this.labelsm;
37472                 items.cls += ' col-sm-' + (12 - this.labelsm);
37473             }
37474             
37475             if(this.labelxs > 0){
37476                 label.cls += ' col-xs-' + this.labelxs;
37477                 items.cls += ' col-xs-' + (12 - this.labelxs);
37478             }
37479         }
37480         
37481         var cfg = {
37482             tag : 'div',
37483             cls : 'roo-radio-set',
37484             cn : [
37485                 {
37486                     tag : 'input',
37487                     cls : 'roo-radio-set-input',
37488                     type : 'hidden',
37489                     name : this.name,
37490                     value : this.value ? this.value :  ''
37491                 },
37492                 label,
37493                 items
37494             ]
37495         };
37496         
37497         if(this.weight.length){
37498             cfg.cls += ' roo-radio-' + this.weight;
37499         }
37500         
37501         if(this.inline) {
37502             cfg.cls += ' roo-radio-set-inline';
37503         }
37504         
37505         var settings=this;
37506         ['xs','sm','md','lg'].map(function(size){
37507             if (settings[size]) {
37508                 cfg.cls += ' col-' + size + '-' + settings[size];
37509             }
37510         });
37511         
37512         return cfg;
37513         
37514     },
37515
37516     initEvents : function()
37517     {
37518         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37519         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37520         
37521         if(!this.fieldLabel.length){
37522             this.labelEl.hide();
37523         }
37524         
37525         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37526         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37527         
37528         this.indicator = this.indicatorEl();
37529         
37530         if(this.indicator){
37531             this.indicator.addClass('invisible');
37532         }
37533         
37534         this.originalValue = this.getValue();
37535         
37536     },
37537     
37538     inputEl: function ()
37539     {
37540         return this.el.select('.roo-radio-set-input', true).first();
37541     },
37542     
37543     getChildContainer : function()
37544     {
37545         return this.itemsEl;
37546     },
37547     
37548     register : function(item)
37549     {
37550         this.radioes.push(item);
37551         
37552     },
37553     
37554     validate : function()
37555     {   
37556         if(this.getVisibilityEl().hasClass('hidden')){
37557             return true;
37558         }
37559         
37560         var valid = false;
37561         
37562         Roo.each(this.radioes, function(i){
37563             if(!i.checked){
37564                 return;
37565             }
37566             
37567             valid = true;
37568             return false;
37569         });
37570         
37571         if(this.allowBlank) {
37572             return true;
37573         }
37574         
37575         if(this.disabled || valid){
37576             this.markValid();
37577             return true;
37578         }
37579         
37580         this.markInvalid();
37581         return false;
37582         
37583     },
37584     
37585     markValid : function()
37586     {
37587         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37588             this.indicatorEl().removeClass('visible');
37589             this.indicatorEl().addClass('invisible');
37590         }
37591         
37592         
37593         if (Roo.bootstrap.version == 3) {
37594             this.el.removeClass([this.invalidClass, this.validClass]);
37595             this.el.addClass(this.validClass);
37596         } else {
37597             this.el.removeClass(['is-invalid','is-valid']);
37598             this.el.addClass(['is-valid']);
37599         }
37600         this.fireEvent('valid', this);
37601     },
37602     
37603     markInvalid : function(msg)
37604     {
37605         if(this.allowBlank || this.disabled){
37606             return;
37607         }
37608         
37609         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37610             this.indicatorEl().removeClass('invisible');
37611             this.indicatorEl().addClass('visible');
37612         }
37613         if (Roo.bootstrap.version == 3) {
37614             this.el.removeClass([this.invalidClass, this.validClass]);
37615             this.el.addClass(this.invalidClass);
37616         } else {
37617             this.el.removeClass(['is-invalid','is-valid']);
37618             this.el.addClass(['is-invalid']);
37619         }
37620         
37621         this.fireEvent('invalid', this, msg);
37622         
37623     },
37624     
37625     setValue : function(v, suppressEvent)
37626     {   
37627         if(this.value === v){
37628             return;
37629         }
37630         
37631         this.value = v;
37632         
37633         if(this.rendered){
37634             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37635         }
37636         
37637         Roo.each(this.radioes, function(i){
37638             i.checked = false;
37639             i.el.removeClass('checked');
37640         });
37641         
37642         Roo.each(this.radioes, function(i){
37643             
37644             if(i.value === v || i.value.toString() === v.toString()){
37645                 i.checked = true;
37646                 i.el.addClass('checked');
37647                 
37648                 if(suppressEvent !== true){
37649                     this.fireEvent('check', this, i);
37650                 }
37651                 
37652                 return false;
37653             }
37654             
37655         }, this);
37656         
37657         this.validate();
37658     },
37659     
37660     clearInvalid : function(){
37661         
37662         if(!this.el || this.preventMark){
37663             return;
37664         }
37665         
37666         this.el.removeClass([this.invalidClass]);
37667         
37668         this.fireEvent('valid', this);
37669     }
37670     
37671 });
37672
37673 Roo.apply(Roo.bootstrap.RadioSet, {
37674     
37675     groups: {},
37676     
37677     register : function(set)
37678     {
37679         this.groups[set.name] = set;
37680     },
37681     
37682     get: function(name) 
37683     {
37684         if (typeof(this.groups[name]) == 'undefined') {
37685             return false;
37686         }
37687         
37688         return this.groups[name] ;
37689     }
37690     
37691 });
37692 /*
37693  * Based on:
37694  * Ext JS Library 1.1.1
37695  * Copyright(c) 2006-2007, Ext JS, LLC.
37696  *
37697  * Originally Released Under LGPL - original licence link has changed is not relivant.
37698  *
37699  * Fork - LGPL
37700  * <script type="text/javascript">
37701  */
37702
37703
37704 /**
37705  * @class Roo.bootstrap.SplitBar
37706  * @extends Roo.util.Observable
37707  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37708  * <br><br>
37709  * Usage:
37710  * <pre><code>
37711 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37712                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37713 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37714 split.minSize = 100;
37715 split.maxSize = 600;
37716 split.animate = true;
37717 split.on('moved', splitterMoved);
37718 </code></pre>
37719  * @constructor
37720  * Create a new SplitBar
37721  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37722  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37723  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37724  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37725                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37726                         position of the SplitBar).
37727  */
37728 Roo.bootstrap.SplitBar = function(cfg){
37729     
37730     /** @private */
37731     
37732     //{
37733     //  dragElement : elm
37734     //  resizingElement: el,
37735         // optional..
37736     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37737     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37738         // existingProxy ???
37739     //}
37740     
37741     this.el = Roo.get(cfg.dragElement, true);
37742     this.el.dom.unselectable = "on";
37743     /** @private */
37744     this.resizingEl = Roo.get(cfg.resizingElement, true);
37745
37746     /**
37747      * @private
37748      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37749      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37750      * @type Number
37751      */
37752     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37753     
37754     /**
37755      * The minimum size of the resizing element. (Defaults to 0)
37756      * @type Number
37757      */
37758     this.minSize = 0;
37759     
37760     /**
37761      * The maximum size of the resizing element. (Defaults to 2000)
37762      * @type Number
37763      */
37764     this.maxSize = 2000;
37765     
37766     /**
37767      * Whether to animate the transition to the new size
37768      * @type Boolean
37769      */
37770     this.animate = false;
37771     
37772     /**
37773      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37774      * @type Boolean
37775      */
37776     this.useShim = false;
37777     
37778     /** @private */
37779     this.shim = null;
37780     
37781     if(!cfg.existingProxy){
37782         /** @private */
37783         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37784     }else{
37785         this.proxy = Roo.get(cfg.existingProxy).dom;
37786     }
37787     /** @private */
37788     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37789     
37790     /** @private */
37791     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37792     
37793     /** @private */
37794     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37795     
37796     /** @private */
37797     this.dragSpecs = {};
37798     
37799     /**
37800      * @private The adapter to use to positon and resize elements
37801      */
37802     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37803     this.adapter.init(this);
37804     
37805     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37806         /** @private */
37807         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37808         this.el.addClass("roo-splitbar-h");
37809     }else{
37810         /** @private */
37811         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37812         this.el.addClass("roo-splitbar-v");
37813     }
37814     
37815     this.addEvents({
37816         /**
37817          * @event resize
37818          * Fires when the splitter is moved (alias for {@link #event-moved})
37819          * @param {Roo.bootstrap.SplitBar} this
37820          * @param {Number} newSize the new width or height
37821          */
37822         "resize" : true,
37823         /**
37824          * @event moved
37825          * Fires when the splitter is moved
37826          * @param {Roo.bootstrap.SplitBar} this
37827          * @param {Number} newSize the new width or height
37828          */
37829         "moved" : true,
37830         /**
37831          * @event beforeresize
37832          * Fires before the splitter is dragged
37833          * @param {Roo.bootstrap.SplitBar} this
37834          */
37835         "beforeresize" : true,
37836
37837         "beforeapply" : true
37838     });
37839
37840     Roo.util.Observable.call(this);
37841 };
37842
37843 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37844     onStartProxyDrag : function(x, y){
37845         this.fireEvent("beforeresize", this);
37846         if(!this.overlay){
37847             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37848             o.unselectable();
37849             o.enableDisplayMode("block");
37850             // all splitbars share the same overlay
37851             Roo.bootstrap.SplitBar.prototype.overlay = o;
37852         }
37853         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37854         this.overlay.show();
37855         Roo.get(this.proxy).setDisplayed("block");
37856         var size = this.adapter.getElementSize(this);
37857         this.activeMinSize = this.getMinimumSize();;
37858         this.activeMaxSize = this.getMaximumSize();;
37859         var c1 = size - this.activeMinSize;
37860         var c2 = Math.max(this.activeMaxSize - size, 0);
37861         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37862             this.dd.resetConstraints();
37863             this.dd.setXConstraint(
37864                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37865                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37866             );
37867             this.dd.setYConstraint(0, 0);
37868         }else{
37869             this.dd.resetConstraints();
37870             this.dd.setXConstraint(0, 0);
37871             this.dd.setYConstraint(
37872                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37873                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37874             );
37875          }
37876         this.dragSpecs.startSize = size;
37877         this.dragSpecs.startPoint = [x, y];
37878         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37879     },
37880     
37881     /** 
37882      * @private Called after the drag operation by the DDProxy
37883      */
37884     onEndProxyDrag : function(e){
37885         Roo.get(this.proxy).setDisplayed(false);
37886         var endPoint = Roo.lib.Event.getXY(e);
37887         if(this.overlay){
37888             this.overlay.hide();
37889         }
37890         var newSize;
37891         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37892             newSize = this.dragSpecs.startSize + 
37893                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37894                     endPoint[0] - this.dragSpecs.startPoint[0] :
37895                     this.dragSpecs.startPoint[0] - endPoint[0]
37896                 );
37897         }else{
37898             newSize = this.dragSpecs.startSize + 
37899                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37900                     endPoint[1] - this.dragSpecs.startPoint[1] :
37901                     this.dragSpecs.startPoint[1] - endPoint[1]
37902                 );
37903         }
37904         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37905         if(newSize != this.dragSpecs.startSize){
37906             if(this.fireEvent('beforeapply', this, newSize) !== false){
37907                 this.adapter.setElementSize(this, newSize);
37908                 this.fireEvent("moved", this, newSize);
37909                 this.fireEvent("resize", this, newSize);
37910             }
37911         }
37912     },
37913     
37914     /**
37915      * Get the adapter this SplitBar uses
37916      * @return The adapter object
37917      */
37918     getAdapter : function(){
37919         return this.adapter;
37920     },
37921     
37922     /**
37923      * Set the adapter this SplitBar uses
37924      * @param {Object} adapter A SplitBar adapter object
37925      */
37926     setAdapter : function(adapter){
37927         this.adapter = adapter;
37928         this.adapter.init(this);
37929     },
37930     
37931     /**
37932      * Gets the minimum size for the resizing element
37933      * @return {Number} The minimum size
37934      */
37935     getMinimumSize : function(){
37936         return this.minSize;
37937     },
37938     
37939     /**
37940      * Sets the minimum size for the resizing element
37941      * @param {Number} minSize The minimum size
37942      */
37943     setMinimumSize : function(minSize){
37944         this.minSize = minSize;
37945     },
37946     
37947     /**
37948      * Gets the maximum size for the resizing element
37949      * @return {Number} The maximum size
37950      */
37951     getMaximumSize : function(){
37952         return this.maxSize;
37953     },
37954     
37955     /**
37956      * Sets the maximum size for the resizing element
37957      * @param {Number} maxSize The maximum size
37958      */
37959     setMaximumSize : function(maxSize){
37960         this.maxSize = maxSize;
37961     },
37962     
37963     /**
37964      * Sets the initialize size for the resizing element
37965      * @param {Number} size The initial size
37966      */
37967     setCurrentSize : function(size){
37968         var oldAnimate = this.animate;
37969         this.animate = false;
37970         this.adapter.setElementSize(this, size);
37971         this.animate = oldAnimate;
37972     },
37973     
37974     /**
37975      * Destroy this splitbar. 
37976      * @param {Boolean} removeEl True to remove the element
37977      */
37978     destroy : function(removeEl){
37979         if(this.shim){
37980             this.shim.remove();
37981         }
37982         this.dd.unreg();
37983         this.proxy.parentNode.removeChild(this.proxy);
37984         if(removeEl){
37985             this.el.remove();
37986         }
37987     }
37988 });
37989
37990 /**
37991  * @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.
37992  */
37993 Roo.bootstrap.SplitBar.createProxy = function(dir){
37994     var proxy = new Roo.Element(document.createElement("div"));
37995     proxy.unselectable();
37996     var cls = 'roo-splitbar-proxy';
37997     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37998     document.body.appendChild(proxy.dom);
37999     return proxy.dom;
38000 };
38001
38002 /** 
38003  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38004  * Default Adapter. It assumes the splitter and resizing element are not positioned
38005  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38006  */
38007 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38008 };
38009
38010 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38011     // do nothing for now
38012     init : function(s){
38013     
38014     },
38015     /**
38016      * Called before drag operations to get the current size of the resizing element. 
38017      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38018      */
38019      getElementSize : function(s){
38020         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38021             return s.resizingEl.getWidth();
38022         }else{
38023             return s.resizingEl.getHeight();
38024         }
38025     },
38026     
38027     /**
38028      * Called after drag operations to set the size of the resizing element.
38029      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38030      * @param {Number} newSize The new size to set
38031      * @param {Function} onComplete A function to be invoked when resizing is complete
38032      */
38033     setElementSize : function(s, newSize, onComplete){
38034         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38035             if(!s.animate){
38036                 s.resizingEl.setWidth(newSize);
38037                 if(onComplete){
38038                     onComplete(s, newSize);
38039                 }
38040             }else{
38041                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38042             }
38043         }else{
38044             
38045             if(!s.animate){
38046                 s.resizingEl.setHeight(newSize);
38047                 if(onComplete){
38048                     onComplete(s, newSize);
38049                 }
38050             }else{
38051                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38052             }
38053         }
38054     }
38055 };
38056
38057 /** 
38058  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38059  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38060  * Adapter that  moves the splitter element to align with the resized sizing element. 
38061  * Used with an absolute positioned SplitBar.
38062  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38063  * document.body, make sure you assign an id to the body element.
38064  */
38065 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38066     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38067     this.container = Roo.get(container);
38068 };
38069
38070 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38071     init : function(s){
38072         this.basic.init(s);
38073     },
38074     
38075     getElementSize : function(s){
38076         return this.basic.getElementSize(s);
38077     },
38078     
38079     setElementSize : function(s, newSize, onComplete){
38080         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38081     },
38082     
38083     moveSplitter : function(s){
38084         var yes = Roo.bootstrap.SplitBar;
38085         switch(s.placement){
38086             case yes.LEFT:
38087                 s.el.setX(s.resizingEl.getRight());
38088                 break;
38089             case yes.RIGHT:
38090                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38091                 break;
38092             case yes.TOP:
38093                 s.el.setY(s.resizingEl.getBottom());
38094                 break;
38095             case yes.BOTTOM:
38096                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38097                 break;
38098         }
38099     }
38100 };
38101
38102 /**
38103  * Orientation constant - Create a vertical SplitBar
38104  * @static
38105  * @type Number
38106  */
38107 Roo.bootstrap.SplitBar.VERTICAL = 1;
38108
38109 /**
38110  * Orientation constant - Create a horizontal SplitBar
38111  * @static
38112  * @type Number
38113  */
38114 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38115
38116 /**
38117  * Placement constant - The resizing element is to the left of the splitter element
38118  * @static
38119  * @type Number
38120  */
38121 Roo.bootstrap.SplitBar.LEFT = 1;
38122
38123 /**
38124  * Placement constant - The resizing element is to the right of the splitter element
38125  * @static
38126  * @type Number
38127  */
38128 Roo.bootstrap.SplitBar.RIGHT = 2;
38129
38130 /**
38131  * Placement constant - The resizing element is positioned above the splitter element
38132  * @static
38133  * @type Number
38134  */
38135 Roo.bootstrap.SplitBar.TOP = 3;
38136
38137 /**
38138  * Placement constant - The resizing element is positioned under splitter element
38139  * @static
38140  * @type Number
38141  */
38142 Roo.bootstrap.SplitBar.BOTTOM = 4;
38143 Roo.namespace("Roo.bootstrap.layout");/*
38144  * Based on:
38145  * Ext JS Library 1.1.1
38146  * Copyright(c) 2006-2007, Ext JS, LLC.
38147  *
38148  * Originally Released Under LGPL - original licence link has changed is not relivant.
38149  *
38150  * Fork - LGPL
38151  * <script type="text/javascript">
38152  */
38153
38154 /**
38155  * @class Roo.bootstrap.layout.Manager
38156  * @extends Roo.bootstrap.Component
38157  * Base class for layout managers.
38158  */
38159 Roo.bootstrap.layout.Manager = function(config)
38160 {
38161     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38162
38163
38164
38165
38166
38167     /** false to disable window resize monitoring @type Boolean */
38168     this.monitorWindowResize = true;
38169     this.regions = {};
38170     this.addEvents({
38171         /**
38172          * @event layout
38173          * Fires when a layout is performed.
38174          * @param {Roo.LayoutManager} this
38175          */
38176         "layout" : true,
38177         /**
38178          * @event regionresized
38179          * Fires when the user resizes a region.
38180          * @param {Roo.LayoutRegion} region The resized region
38181          * @param {Number} newSize The new size (width for east/west, height for north/south)
38182          */
38183         "regionresized" : true,
38184         /**
38185          * @event regioncollapsed
38186          * Fires when a region is collapsed.
38187          * @param {Roo.LayoutRegion} region The collapsed region
38188          */
38189         "regioncollapsed" : true,
38190         /**
38191          * @event regionexpanded
38192          * Fires when a region is expanded.
38193          * @param {Roo.LayoutRegion} region The expanded region
38194          */
38195         "regionexpanded" : true
38196     });
38197     this.updating = false;
38198
38199     if (config.el) {
38200         this.el = Roo.get(config.el);
38201         this.initEvents();
38202     }
38203
38204 };
38205
38206 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38207
38208
38209     regions : null,
38210
38211     monitorWindowResize : true,
38212
38213
38214     updating : false,
38215
38216
38217     onRender : function(ct, position)
38218     {
38219         if(!this.el){
38220             this.el = Roo.get(ct);
38221             this.initEvents();
38222         }
38223         //this.fireEvent('render',this);
38224     },
38225
38226
38227     initEvents: function()
38228     {
38229
38230
38231         // ie scrollbar fix
38232         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38233             document.body.scroll = "no";
38234         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38235             this.el.position('relative');
38236         }
38237         this.id = this.el.id;
38238         this.el.addClass("roo-layout-container");
38239         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38240         if(this.el.dom != document.body ) {
38241             this.el.on('resize', this.layout,this);
38242             this.el.on('show', this.layout,this);
38243         }
38244
38245     },
38246
38247     /**
38248      * Returns true if this layout is currently being updated
38249      * @return {Boolean}
38250      */
38251     isUpdating : function(){
38252         return this.updating;
38253     },
38254
38255     /**
38256      * Suspend the LayoutManager from doing auto-layouts while
38257      * making multiple add or remove calls
38258      */
38259     beginUpdate : function(){
38260         this.updating = true;
38261     },
38262
38263     /**
38264      * Restore auto-layouts and optionally disable the manager from performing a layout
38265      * @param {Boolean} noLayout true to disable a layout update
38266      */
38267     endUpdate : function(noLayout){
38268         this.updating = false;
38269         if(!noLayout){
38270             this.layout();
38271         }
38272     },
38273
38274     layout: function(){
38275         // abstract...
38276     },
38277
38278     onRegionResized : function(region, newSize){
38279         this.fireEvent("regionresized", region, newSize);
38280         this.layout();
38281     },
38282
38283     onRegionCollapsed : function(region){
38284         this.fireEvent("regioncollapsed", region);
38285     },
38286
38287     onRegionExpanded : function(region){
38288         this.fireEvent("regionexpanded", region);
38289     },
38290
38291     /**
38292      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38293      * performs box-model adjustments.
38294      * @return {Object} The size as an object {width: (the width), height: (the height)}
38295      */
38296     getViewSize : function()
38297     {
38298         var size;
38299         if(this.el.dom != document.body){
38300             size = this.el.getSize();
38301         }else{
38302             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38303         }
38304         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38305         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38306         return size;
38307     },
38308
38309     /**
38310      * Returns the Element this layout is bound to.
38311      * @return {Roo.Element}
38312      */
38313     getEl : function(){
38314         return this.el;
38315     },
38316
38317     /**
38318      * Returns the specified region.
38319      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38320      * @return {Roo.LayoutRegion}
38321      */
38322     getRegion : function(target){
38323         return this.regions[target.toLowerCase()];
38324     },
38325
38326     onWindowResize : function(){
38327         if(this.monitorWindowResize){
38328             this.layout();
38329         }
38330     }
38331 });
38332 /*
38333  * Based on:
38334  * Ext JS Library 1.1.1
38335  * Copyright(c) 2006-2007, Ext JS, LLC.
38336  *
38337  * Originally Released Under LGPL - original licence link has changed is not relivant.
38338  *
38339  * Fork - LGPL
38340  * <script type="text/javascript">
38341  */
38342 /**
38343  * @class Roo.bootstrap.layout.Border
38344  * @extends Roo.bootstrap.layout.Manager
38345  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38346  * please see: examples/bootstrap/nested.html<br><br>
38347  
38348 <b>The container the layout is rendered into can be either the body element or any other element.
38349 If it is not the body element, the container needs to either be an absolute positioned element,
38350 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38351 the container size if it is not the body element.</b>
38352
38353 * @constructor
38354 * Create a new Border
38355 * @param {Object} config Configuration options
38356  */
38357 Roo.bootstrap.layout.Border = function(config){
38358     config = config || {};
38359     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38360     
38361     
38362     
38363     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38364         if(config[region]){
38365             config[region].region = region;
38366             this.addRegion(config[region]);
38367         }
38368     },this);
38369     
38370 };
38371
38372 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38373
38374 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38375     
38376     parent : false, // this might point to a 'nest' or a ???
38377     
38378     /**
38379      * Creates and adds a new region if it doesn't already exist.
38380      * @param {String} target The target region key (north, south, east, west or center).
38381      * @param {Object} config The regions config object
38382      * @return {BorderLayoutRegion} The new region
38383      */
38384     addRegion : function(config)
38385     {
38386         if(!this.regions[config.region]){
38387             var r = this.factory(config);
38388             this.bindRegion(r);
38389         }
38390         return this.regions[config.region];
38391     },
38392
38393     // private (kinda)
38394     bindRegion : function(r){
38395         this.regions[r.config.region] = r;
38396         
38397         r.on("visibilitychange",    this.layout, this);
38398         r.on("paneladded",          this.layout, this);
38399         r.on("panelremoved",        this.layout, this);
38400         r.on("invalidated",         this.layout, this);
38401         r.on("resized",             this.onRegionResized, this);
38402         r.on("collapsed",           this.onRegionCollapsed, this);
38403         r.on("expanded",            this.onRegionExpanded, this);
38404     },
38405
38406     /**
38407      * Performs a layout update.
38408      */
38409     layout : function()
38410     {
38411         if(this.updating) {
38412             return;
38413         }
38414         
38415         // render all the rebions if they have not been done alreayd?
38416         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38417             if(this.regions[region] && !this.regions[region].bodyEl){
38418                 this.regions[region].onRender(this.el)
38419             }
38420         },this);
38421         
38422         var size = this.getViewSize();
38423         var w = size.width;
38424         var h = size.height;
38425         var centerW = w;
38426         var centerH = h;
38427         var centerY = 0;
38428         var centerX = 0;
38429         //var x = 0, y = 0;
38430
38431         var rs = this.regions;
38432         var north = rs["north"];
38433         var south = rs["south"]; 
38434         var west = rs["west"];
38435         var east = rs["east"];
38436         var center = rs["center"];
38437         //if(this.hideOnLayout){ // not supported anymore
38438             //c.el.setStyle("display", "none");
38439         //}
38440         if(north && north.isVisible()){
38441             var b = north.getBox();
38442             var m = north.getMargins();
38443             b.width = w - (m.left+m.right);
38444             b.x = m.left;
38445             b.y = m.top;
38446             centerY = b.height + b.y + m.bottom;
38447             centerH -= centerY;
38448             north.updateBox(this.safeBox(b));
38449         }
38450         if(south && south.isVisible()){
38451             var b = south.getBox();
38452             var m = south.getMargins();
38453             b.width = w - (m.left+m.right);
38454             b.x = m.left;
38455             var totalHeight = (b.height + m.top + m.bottom);
38456             b.y = h - totalHeight + m.top;
38457             centerH -= totalHeight;
38458             south.updateBox(this.safeBox(b));
38459         }
38460         if(west && west.isVisible()){
38461             var b = west.getBox();
38462             var m = west.getMargins();
38463             b.height = centerH - (m.top+m.bottom);
38464             b.x = m.left;
38465             b.y = centerY + m.top;
38466             var totalWidth = (b.width + m.left + m.right);
38467             centerX += totalWidth;
38468             centerW -= totalWidth;
38469             west.updateBox(this.safeBox(b));
38470         }
38471         if(east && east.isVisible()){
38472             var b = east.getBox();
38473             var m = east.getMargins();
38474             b.height = centerH - (m.top+m.bottom);
38475             var totalWidth = (b.width + m.left + m.right);
38476             b.x = w - totalWidth + m.left;
38477             b.y = centerY + m.top;
38478             centerW -= totalWidth;
38479             east.updateBox(this.safeBox(b));
38480         }
38481         if(center){
38482             var m = center.getMargins();
38483             var centerBox = {
38484                 x: centerX + m.left,
38485                 y: centerY + m.top,
38486                 width: centerW - (m.left+m.right),
38487                 height: centerH - (m.top+m.bottom)
38488             };
38489             //if(this.hideOnLayout){
38490                 //center.el.setStyle("display", "block");
38491             //}
38492             center.updateBox(this.safeBox(centerBox));
38493         }
38494         this.el.repaint();
38495         this.fireEvent("layout", this);
38496     },
38497
38498     // private
38499     safeBox : function(box){
38500         box.width = Math.max(0, box.width);
38501         box.height = Math.max(0, box.height);
38502         return box;
38503     },
38504
38505     /**
38506      * Adds a ContentPanel (or subclass) to this layout.
38507      * @param {String} target The target region key (north, south, east, west or center).
38508      * @param {Roo.ContentPanel} panel The panel to add
38509      * @return {Roo.ContentPanel} The added panel
38510      */
38511     add : function(target, panel){
38512          
38513         target = target.toLowerCase();
38514         return this.regions[target].add(panel);
38515     },
38516
38517     /**
38518      * Remove a ContentPanel (or subclass) to this layout.
38519      * @param {String} target The target region key (north, south, east, west or center).
38520      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38521      * @return {Roo.ContentPanel} The removed panel
38522      */
38523     remove : function(target, panel){
38524         target = target.toLowerCase();
38525         return this.regions[target].remove(panel);
38526     },
38527
38528     /**
38529      * Searches all regions for a panel with the specified id
38530      * @param {String} panelId
38531      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38532      */
38533     findPanel : function(panelId){
38534         var rs = this.regions;
38535         for(var target in rs){
38536             if(typeof rs[target] != "function"){
38537                 var p = rs[target].getPanel(panelId);
38538                 if(p){
38539                     return p;
38540                 }
38541             }
38542         }
38543         return null;
38544     },
38545
38546     /**
38547      * Searches all regions for a panel with the specified id and activates (shows) it.
38548      * @param {String/ContentPanel} panelId The panels id or the panel itself
38549      * @return {Roo.ContentPanel} The shown panel or null
38550      */
38551     showPanel : function(panelId) {
38552       var rs = this.regions;
38553       for(var target in rs){
38554          var r = rs[target];
38555          if(typeof r != "function"){
38556             if(r.hasPanel(panelId)){
38557                return r.showPanel(panelId);
38558             }
38559          }
38560       }
38561       return null;
38562    },
38563
38564    /**
38565      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38566      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38567      */
38568    /*
38569     restoreState : function(provider){
38570         if(!provider){
38571             provider = Roo.state.Manager;
38572         }
38573         var sm = new Roo.LayoutStateManager();
38574         sm.init(this, provider);
38575     },
38576 */
38577  
38578  
38579     /**
38580      * Adds a xtype elements to the layout.
38581      * <pre><code>
38582
38583 layout.addxtype({
38584        xtype : 'ContentPanel',
38585        region: 'west',
38586        items: [ .... ]
38587    }
38588 );
38589
38590 layout.addxtype({
38591         xtype : 'NestedLayoutPanel',
38592         region: 'west',
38593         layout: {
38594            center: { },
38595            west: { }   
38596         },
38597         items : [ ... list of content panels or nested layout panels.. ]
38598    }
38599 );
38600 </code></pre>
38601      * @param {Object} cfg Xtype definition of item to add.
38602      */
38603     addxtype : function(cfg)
38604     {
38605         // basically accepts a pannel...
38606         // can accept a layout region..!?!?
38607         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38608         
38609         
38610         // theory?  children can only be panels??
38611         
38612         //if (!cfg.xtype.match(/Panel$/)) {
38613         //    return false;
38614         //}
38615         var ret = false;
38616         
38617         if (typeof(cfg.region) == 'undefined') {
38618             Roo.log("Failed to add Panel, region was not set");
38619             Roo.log(cfg);
38620             return false;
38621         }
38622         var region = cfg.region;
38623         delete cfg.region;
38624         
38625           
38626         var xitems = [];
38627         if (cfg.items) {
38628             xitems = cfg.items;
38629             delete cfg.items;
38630         }
38631         var nb = false;
38632         
38633         if ( region == 'center') {
38634             Roo.log("Center: " + cfg.title);
38635         }
38636         
38637         
38638         switch(cfg.xtype) 
38639         {
38640             case 'Content':  // ContentPanel (el, cfg)
38641             case 'Scroll':  // ContentPanel (el, cfg)
38642             case 'View': 
38643                 cfg.autoCreate = cfg.autoCreate || true;
38644                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38645                 //} else {
38646                 //    var el = this.el.createChild();
38647                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38648                 //}
38649                 
38650                 this.add(region, ret);
38651                 break;
38652             
38653             /*
38654             case 'TreePanel': // our new panel!
38655                 cfg.el = this.el.createChild();
38656                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38657                 this.add(region, ret);
38658                 break;
38659             */
38660             
38661             case 'Nest': 
38662                 // create a new Layout (which is  a Border Layout...
38663                 
38664                 var clayout = cfg.layout;
38665                 clayout.el  = this.el.createChild();
38666                 clayout.items   = clayout.items  || [];
38667                 
38668                 delete cfg.layout;
38669                 
38670                 // replace this exitems with the clayout ones..
38671                 xitems = clayout.items;
38672                  
38673                 // force background off if it's in center...
38674                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38675                     cfg.background = false;
38676                 }
38677                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38678                 
38679                 
38680                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38681                 //console.log('adding nested layout panel '  + cfg.toSource());
38682                 this.add(region, ret);
38683                 nb = {}; /// find first...
38684                 break;
38685             
38686             case 'Grid':
38687                 
38688                 // needs grid and region
38689                 
38690                 //var el = this.getRegion(region).el.createChild();
38691                 /*
38692                  *var el = this.el.createChild();
38693                 // create the grid first...
38694                 cfg.grid.container = el;
38695                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38696                 */
38697                 
38698                 if (region == 'center' && this.active ) {
38699                     cfg.background = false;
38700                 }
38701                 
38702                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38703                 
38704                 this.add(region, ret);
38705                 /*
38706                 if (cfg.background) {
38707                     // render grid on panel activation (if panel background)
38708                     ret.on('activate', function(gp) {
38709                         if (!gp.grid.rendered) {
38710                     //        gp.grid.render(el);
38711                         }
38712                     });
38713                 } else {
38714                   //  cfg.grid.render(el);
38715                 }
38716                 */
38717                 break;
38718            
38719            
38720             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38721                 // it was the old xcomponent building that caused this before.
38722                 // espeically if border is the top element in the tree.
38723                 ret = this;
38724                 break; 
38725                 
38726                     
38727                 
38728                 
38729                 
38730             default:
38731                 /*
38732                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38733                     
38734                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38735                     this.add(region, ret);
38736                 } else {
38737                 */
38738                     Roo.log(cfg);
38739                     throw "Can not add '" + cfg.xtype + "' to Border";
38740                     return null;
38741              
38742                                 
38743              
38744         }
38745         this.beginUpdate();
38746         // add children..
38747         var region = '';
38748         var abn = {};
38749         Roo.each(xitems, function(i)  {
38750             region = nb && i.region ? i.region : false;
38751             
38752             var add = ret.addxtype(i);
38753            
38754             if (region) {
38755                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38756                 if (!i.background) {
38757                     abn[region] = nb[region] ;
38758                 }
38759             }
38760             
38761         });
38762         this.endUpdate();
38763
38764         // make the last non-background panel active..
38765         //if (nb) { Roo.log(abn); }
38766         if (nb) {
38767             
38768             for(var r in abn) {
38769                 region = this.getRegion(r);
38770                 if (region) {
38771                     // tried using nb[r], but it does not work..
38772                      
38773                     region.showPanel(abn[r]);
38774                    
38775                 }
38776             }
38777         }
38778         return ret;
38779         
38780     },
38781     
38782     
38783 // private
38784     factory : function(cfg)
38785     {
38786         
38787         var validRegions = Roo.bootstrap.layout.Border.regions;
38788
38789         var target = cfg.region;
38790         cfg.mgr = this;
38791         
38792         var r = Roo.bootstrap.layout;
38793         Roo.log(target);
38794         switch(target){
38795             case "north":
38796                 return new r.North(cfg);
38797             case "south":
38798                 return new r.South(cfg);
38799             case "east":
38800                 return new r.East(cfg);
38801             case "west":
38802                 return new r.West(cfg);
38803             case "center":
38804                 return new r.Center(cfg);
38805         }
38806         throw 'Layout region "'+target+'" not supported.';
38807     }
38808     
38809     
38810 });
38811  /*
38812  * Based on:
38813  * Ext JS Library 1.1.1
38814  * Copyright(c) 2006-2007, Ext JS, LLC.
38815  *
38816  * Originally Released Under LGPL - original licence link has changed is not relivant.
38817  *
38818  * Fork - LGPL
38819  * <script type="text/javascript">
38820  */
38821  
38822 /**
38823  * @class Roo.bootstrap.layout.Basic
38824  * @extends Roo.util.Observable
38825  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38826  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38827  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38828  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38829  * @cfg {string}   region  the region that it inhabits..
38830  * @cfg {bool}   skipConfig skip config?
38831  * 
38832
38833  */
38834 Roo.bootstrap.layout.Basic = function(config){
38835     
38836     this.mgr = config.mgr;
38837     
38838     this.position = config.region;
38839     
38840     var skipConfig = config.skipConfig;
38841     
38842     this.events = {
38843         /**
38844          * @scope Roo.BasicLayoutRegion
38845          */
38846         
38847         /**
38848          * @event beforeremove
38849          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38850          * @param {Roo.LayoutRegion} this
38851          * @param {Roo.ContentPanel} panel The panel
38852          * @param {Object} e The cancel event object
38853          */
38854         "beforeremove" : true,
38855         /**
38856          * @event invalidated
38857          * Fires when the layout for this region is changed.
38858          * @param {Roo.LayoutRegion} this
38859          */
38860         "invalidated" : true,
38861         /**
38862          * @event visibilitychange
38863          * Fires when this region is shown or hidden 
38864          * @param {Roo.LayoutRegion} this
38865          * @param {Boolean} visibility true or false
38866          */
38867         "visibilitychange" : true,
38868         /**
38869          * @event paneladded
38870          * Fires when a panel is added. 
38871          * @param {Roo.LayoutRegion} this
38872          * @param {Roo.ContentPanel} panel The panel
38873          */
38874         "paneladded" : true,
38875         /**
38876          * @event panelremoved
38877          * Fires when a panel is removed. 
38878          * @param {Roo.LayoutRegion} this
38879          * @param {Roo.ContentPanel} panel The panel
38880          */
38881         "panelremoved" : true,
38882         /**
38883          * @event beforecollapse
38884          * Fires when this region before collapse.
38885          * @param {Roo.LayoutRegion} this
38886          */
38887         "beforecollapse" : true,
38888         /**
38889          * @event collapsed
38890          * Fires when this region is collapsed.
38891          * @param {Roo.LayoutRegion} this
38892          */
38893         "collapsed" : true,
38894         /**
38895          * @event expanded
38896          * Fires when this region is expanded.
38897          * @param {Roo.LayoutRegion} this
38898          */
38899         "expanded" : true,
38900         /**
38901          * @event slideshow
38902          * Fires when this region is slid into view.
38903          * @param {Roo.LayoutRegion} this
38904          */
38905         "slideshow" : true,
38906         /**
38907          * @event slidehide
38908          * Fires when this region slides out of view. 
38909          * @param {Roo.LayoutRegion} this
38910          */
38911         "slidehide" : true,
38912         /**
38913          * @event panelactivated
38914          * Fires when a panel is activated. 
38915          * @param {Roo.LayoutRegion} this
38916          * @param {Roo.ContentPanel} panel The activated panel
38917          */
38918         "panelactivated" : true,
38919         /**
38920          * @event resized
38921          * Fires when the user resizes this region. 
38922          * @param {Roo.LayoutRegion} this
38923          * @param {Number} newSize The new size (width for east/west, height for north/south)
38924          */
38925         "resized" : true
38926     };
38927     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38928     this.panels = new Roo.util.MixedCollection();
38929     this.panels.getKey = this.getPanelId.createDelegate(this);
38930     this.box = null;
38931     this.activePanel = null;
38932     // ensure listeners are added...
38933     
38934     if (config.listeners || config.events) {
38935         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38936             listeners : config.listeners || {},
38937             events : config.events || {}
38938         });
38939     }
38940     
38941     if(skipConfig !== true){
38942         this.applyConfig(config);
38943     }
38944 };
38945
38946 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38947 {
38948     getPanelId : function(p){
38949         return p.getId();
38950     },
38951     
38952     applyConfig : function(config){
38953         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38954         this.config = config;
38955         
38956     },
38957     
38958     /**
38959      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38960      * the width, for horizontal (north, south) the height.
38961      * @param {Number} newSize The new width or height
38962      */
38963     resizeTo : function(newSize){
38964         var el = this.el ? this.el :
38965                  (this.activePanel ? this.activePanel.getEl() : null);
38966         if(el){
38967             switch(this.position){
38968                 case "east":
38969                 case "west":
38970                     el.setWidth(newSize);
38971                     this.fireEvent("resized", this, newSize);
38972                 break;
38973                 case "north":
38974                 case "south":
38975                     el.setHeight(newSize);
38976                     this.fireEvent("resized", this, newSize);
38977                 break;                
38978             }
38979         }
38980     },
38981     
38982     getBox : function(){
38983         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38984     },
38985     
38986     getMargins : function(){
38987         return this.margins;
38988     },
38989     
38990     updateBox : function(box){
38991         this.box = box;
38992         var el = this.activePanel.getEl();
38993         el.dom.style.left = box.x + "px";
38994         el.dom.style.top = box.y + "px";
38995         this.activePanel.setSize(box.width, box.height);
38996     },
38997     
38998     /**
38999      * Returns the container element for this region.
39000      * @return {Roo.Element}
39001      */
39002     getEl : function(){
39003         return this.activePanel;
39004     },
39005     
39006     /**
39007      * Returns true if this region is currently visible.
39008      * @return {Boolean}
39009      */
39010     isVisible : function(){
39011         return this.activePanel ? true : false;
39012     },
39013     
39014     setActivePanel : function(panel){
39015         panel = this.getPanel(panel);
39016         if(this.activePanel && this.activePanel != panel){
39017             this.activePanel.setActiveState(false);
39018             this.activePanel.getEl().setLeftTop(-10000,-10000);
39019         }
39020         this.activePanel = panel;
39021         panel.setActiveState(true);
39022         if(this.box){
39023             panel.setSize(this.box.width, this.box.height);
39024         }
39025         this.fireEvent("panelactivated", this, panel);
39026         this.fireEvent("invalidated");
39027     },
39028     
39029     /**
39030      * Show the specified panel.
39031      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39032      * @return {Roo.ContentPanel} The shown panel or null
39033      */
39034     showPanel : function(panel){
39035         panel = this.getPanel(panel);
39036         if(panel){
39037             this.setActivePanel(panel);
39038         }
39039         return panel;
39040     },
39041     
39042     /**
39043      * Get the active panel for this region.
39044      * @return {Roo.ContentPanel} The active panel or null
39045      */
39046     getActivePanel : function(){
39047         return this.activePanel;
39048     },
39049     
39050     /**
39051      * Add the passed ContentPanel(s)
39052      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39053      * @return {Roo.ContentPanel} The panel added (if only one was added)
39054      */
39055     add : function(panel){
39056         if(arguments.length > 1){
39057             for(var i = 0, len = arguments.length; i < len; i++) {
39058                 this.add(arguments[i]);
39059             }
39060             return null;
39061         }
39062         if(this.hasPanel(panel)){
39063             this.showPanel(panel);
39064             return panel;
39065         }
39066         var el = panel.getEl();
39067         if(el.dom.parentNode != this.mgr.el.dom){
39068             this.mgr.el.dom.appendChild(el.dom);
39069         }
39070         if(panel.setRegion){
39071             panel.setRegion(this);
39072         }
39073         this.panels.add(panel);
39074         el.setStyle("position", "absolute");
39075         if(!panel.background){
39076             this.setActivePanel(panel);
39077             if(this.config.initialSize && this.panels.getCount()==1){
39078                 this.resizeTo(this.config.initialSize);
39079             }
39080         }
39081         this.fireEvent("paneladded", this, panel);
39082         return panel;
39083     },
39084     
39085     /**
39086      * Returns true if the panel is in this region.
39087      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39088      * @return {Boolean}
39089      */
39090     hasPanel : function(panel){
39091         if(typeof panel == "object"){ // must be panel obj
39092             panel = panel.getId();
39093         }
39094         return this.getPanel(panel) ? true : false;
39095     },
39096     
39097     /**
39098      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39099      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39100      * @param {Boolean} preservePanel Overrides the config preservePanel option
39101      * @return {Roo.ContentPanel} The panel that was removed
39102      */
39103     remove : function(panel, preservePanel){
39104         panel = this.getPanel(panel);
39105         if(!panel){
39106             return null;
39107         }
39108         var e = {};
39109         this.fireEvent("beforeremove", this, panel, e);
39110         if(e.cancel === true){
39111             return null;
39112         }
39113         var panelId = panel.getId();
39114         this.panels.removeKey(panelId);
39115         return panel;
39116     },
39117     
39118     /**
39119      * Returns the panel specified or null if it's not in this region.
39120      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39121      * @return {Roo.ContentPanel}
39122      */
39123     getPanel : function(id){
39124         if(typeof id == "object"){ // must be panel obj
39125             return id;
39126         }
39127         return this.panels.get(id);
39128     },
39129     
39130     /**
39131      * Returns this regions position (north/south/east/west/center).
39132      * @return {String} 
39133      */
39134     getPosition: function(){
39135         return this.position;    
39136     }
39137 });/*
39138  * Based on:
39139  * Ext JS Library 1.1.1
39140  * Copyright(c) 2006-2007, Ext JS, LLC.
39141  *
39142  * Originally Released Under LGPL - original licence link has changed is not relivant.
39143  *
39144  * Fork - LGPL
39145  * <script type="text/javascript">
39146  */
39147  
39148 /**
39149  * @class Roo.bootstrap.layout.Region
39150  * @extends Roo.bootstrap.layout.Basic
39151  * This class represents a region in a layout manager.
39152  
39153  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39154  * @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})
39155  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39156  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39157  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39158  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39159  * @cfg {String}    title           The title for the region (overrides panel titles)
39160  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39161  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39162  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39163  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39164  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39165  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39166  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39167  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39168  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39169  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39170
39171  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39172  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39173  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39174  * @cfg {Number}    width           For East/West panels
39175  * @cfg {Number}    height          For North/South panels
39176  * @cfg {Boolean}   split           To show the splitter
39177  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39178  * 
39179  * @cfg {string}   cls             Extra CSS classes to add to region
39180  * 
39181  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39182  * @cfg {string}   region  the region that it inhabits..
39183  *
39184
39185  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39186  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39187
39188  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39189  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39190  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39191  */
39192 Roo.bootstrap.layout.Region = function(config)
39193 {
39194     this.applyConfig(config);
39195
39196     var mgr = config.mgr;
39197     var pos = config.region;
39198     config.skipConfig = true;
39199     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39200     
39201     if (mgr.el) {
39202         this.onRender(mgr.el);   
39203     }
39204      
39205     this.visible = true;
39206     this.collapsed = false;
39207     this.unrendered_panels = [];
39208 };
39209
39210 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39211
39212     position: '', // set by wrapper (eg. north/south etc..)
39213     unrendered_panels : null,  // unrendered panels.
39214     
39215     tabPosition : false,
39216     
39217     mgr: false, // points to 'Border'
39218     
39219     
39220     createBody : function(){
39221         /** This region's body element 
39222         * @type Roo.Element */
39223         this.bodyEl = this.el.createChild({
39224                 tag: "div",
39225                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39226         });
39227     },
39228
39229     onRender: function(ctr, pos)
39230     {
39231         var dh = Roo.DomHelper;
39232         /** This region's container element 
39233         * @type Roo.Element */
39234         this.el = dh.append(ctr.dom, {
39235                 tag: "div",
39236                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39237             }, true);
39238         /** This region's title element 
39239         * @type Roo.Element */
39240     
39241         this.titleEl = dh.append(this.el.dom,  {
39242                 tag: "div",
39243                 unselectable: "on",
39244                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39245                 children:[
39246                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39247                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39248                 ]
39249             }, true);
39250         
39251         this.titleEl.enableDisplayMode();
39252         /** This region's title text element 
39253         * @type HTMLElement */
39254         this.titleTextEl = this.titleEl.dom.firstChild;
39255         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39256         /*
39257         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39258         this.closeBtn.enableDisplayMode();
39259         this.closeBtn.on("click", this.closeClicked, this);
39260         this.closeBtn.hide();
39261     */
39262         this.createBody(this.config);
39263         if(this.config.hideWhenEmpty){
39264             this.hide();
39265             this.on("paneladded", this.validateVisibility, this);
39266             this.on("panelremoved", this.validateVisibility, this);
39267         }
39268         if(this.autoScroll){
39269             this.bodyEl.setStyle("overflow", "auto");
39270         }else{
39271             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39272         }
39273         //if(c.titlebar !== false){
39274             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39275                 this.titleEl.hide();
39276             }else{
39277                 this.titleEl.show();
39278                 if(this.config.title){
39279                     this.titleTextEl.innerHTML = this.config.title;
39280                 }
39281             }
39282         //}
39283         if(this.config.collapsed){
39284             this.collapse(true);
39285         }
39286         if(this.config.hidden){
39287             this.hide();
39288         }
39289         
39290         if (this.unrendered_panels && this.unrendered_panels.length) {
39291             for (var i =0;i< this.unrendered_panels.length; i++) {
39292                 this.add(this.unrendered_panels[i]);
39293             }
39294             this.unrendered_panels = null;
39295             
39296         }
39297         
39298     },
39299     
39300     applyConfig : function(c)
39301     {
39302         /*
39303          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39304             var dh = Roo.DomHelper;
39305             if(c.titlebar !== false){
39306                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39307                 this.collapseBtn.on("click", this.collapse, this);
39308                 this.collapseBtn.enableDisplayMode();
39309                 /*
39310                 if(c.showPin === true || this.showPin){
39311                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39312                     this.stickBtn.enableDisplayMode();
39313                     this.stickBtn.on("click", this.expand, this);
39314                     this.stickBtn.hide();
39315                 }
39316                 
39317             }
39318             */
39319             /** This region's collapsed element
39320             * @type Roo.Element */
39321             /*
39322              *
39323             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39324                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39325             ]}, true);
39326             
39327             if(c.floatable !== false){
39328                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39329                this.collapsedEl.on("click", this.collapseClick, this);
39330             }
39331
39332             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39333                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39334                    id: "message", unselectable: "on", style:{"float":"left"}});
39335                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39336              }
39337             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39338             this.expandBtn.on("click", this.expand, this);
39339             
39340         }
39341         
39342         if(this.collapseBtn){
39343             this.collapseBtn.setVisible(c.collapsible == true);
39344         }
39345         
39346         this.cmargins = c.cmargins || this.cmargins ||
39347                          (this.position == "west" || this.position == "east" ?
39348                              {top: 0, left: 2, right:2, bottom: 0} :
39349                              {top: 2, left: 0, right:0, bottom: 2});
39350         */
39351         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39352         
39353         
39354         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39355         
39356         this.autoScroll = c.autoScroll || false;
39357         
39358         
39359        
39360         
39361         this.duration = c.duration || .30;
39362         this.slideDuration = c.slideDuration || .45;
39363         this.config = c;
39364        
39365     },
39366     /**
39367      * Returns true if this region is currently visible.
39368      * @return {Boolean}
39369      */
39370     isVisible : function(){
39371         return this.visible;
39372     },
39373
39374     /**
39375      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39376      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39377      */
39378     //setCollapsedTitle : function(title){
39379     //    title = title || "&#160;";
39380      //   if(this.collapsedTitleTextEl){
39381       //      this.collapsedTitleTextEl.innerHTML = title;
39382        // }
39383     //},
39384
39385     getBox : function(){
39386         var b;
39387       //  if(!this.collapsed){
39388             b = this.el.getBox(false, true);
39389        // }else{
39390           //  b = this.collapsedEl.getBox(false, true);
39391         //}
39392         return b;
39393     },
39394
39395     getMargins : function(){
39396         return this.margins;
39397         //return this.collapsed ? this.cmargins : this.margins;
39398     },
39399 /*
39400     highlight : function(){
39401         this.el.addClass("x-layout-panel-dragover");
39402     },
39403
39404     unhighlight : function(){
39405         this.el.removeClass("x-layout-panel-dragover");
39406     },
39407 */
39408     updateBox : function(box)
39409     {
39410         if (!this.bodyEl) {
39411             return; // not rendered yet..
39412         }
39413         
39414         this.box = box;
39415         if(!this.collapsed){
39416             this.el.dom.style.left = box.x + "px";
39417             this.el.dom.style.top = box.y + "px";
39418             this.updateBody(box.width, box.height);
39419         }else{
39420             this.collapsedEl.dom.style.left = box.x + "px";
39421             this.collapsedEl.dom.style.top = box.y + "px";
39422             this.collapsedEl.setSize(box.width, box.height);
39423         }
39424         if(this.tabs){
39425             this.tabs.autoSizeTabs();
39426         }
39427     },
39428
39429     updateBody : function(w, h)
39430     {
39431         if(w !== null){
39432             this.el.setWidth(w);
39433             w -= this.el.getBorderWidth("rl");
39434             if(this.config.adjustments){
39435                 w += this.config.adjustments[0];
39436             }
39437         }
39438         if(h !== null && h > 0){
39439             this.el.setHeight(h);
39440             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39441             h -= this.el.getBorderWidth("tb");
39442             if(this.config.adjustments){
39443                 h += this.config.adjustments[1];
39444             }
39445             this.bodyEl.setHeight(h);
39446             if(this.tabs){
39447                 h = this.tabs.syncHeight(h);
39448             }
39449         }
39450         if(this.panelSize){
39451             w = w !== null ? w : this.panelSize.width;
39452             h = h !== null ? h : this.panelSize.height;
39453         }
39454         if(this.activePanel){
39455             var el = this.activePanel.getEl();
39456             w = w !== null ? w : el.getWidth();
39457             h = h !== null ? h : el.getHeight();
39458             this.panelSize = {width: w, height: h};
39459             this.activePanel.setSize(w, h);
39460         }
39461         if(Roo.isIE && this.tabs){
39462             this.tabs.el.repaint();
39463         }
39464     },
39465
39466     /**
39467      * Returns the container element for this region.
39468      * @return {Roo.Element}
39469      */
39470     getEl : function(){
39471         return this.el;
39472     },
39473
39474     /**
39475      * Hides this region.
39476      */
39477     hide : function(){
39478         //if(!this.collapsed){
39479             this.el.dom.style.left = "-2000px";
39480             this.el.hide();
39481         //}else{
39482          //   this.collapsedEl.dom.style.left = "-2000px";
39483          //   this.collapsedEl.hide();
39484        // }
39485         this.visible = false;
39486         this.fireEvent("visibilitychange", this, false);
39487     },
39488
39489     /**
39490      * Shows this region if it was previously hidden.
39491      */
39492     show : function(){
39493         //if(!this.collapsed){
39494             this.el.show();
39495         //}else{
39496         //    this.collapsedEl.show();
39497        // }
39498         this.visible = true;
39499         this.fireEvent("visibilitychange", this, true);
39500     },
39501 /*
39502     closeClicked : function(){
39503         if(this.activePanel){
39504             this.remove(this.activePanel);
39505         }
39506     },
39507
39508     collapseClick : function(e){
39509         if(this.isSlid){
39510            e.stopPropagation();
39511            this.slideIn();
39512         }else{
39513            e.stopPropagation();
39514            this.slideOut();
39515         }
39516     },
39517 */
39518     /**
39519      * Collapses this region.
39520      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39521      */
39522     /*
39523     collapse : function(skipAnim, skipCheck = false){
39524         if(this.collapsed) {
39525             return;
39526         }
39527         
39528         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39529             
39530             this.collapsed = true;
39531             if(this.split){
39532                 this.split.el.hide();
39533             }
39534             if(this.config.animate && skipAnim !== true){
39535                 this.fireEvent("invalidated", this);
39536                 this.animateCollapse();
39537             }else{
39538                 this.el.setLocation(-20000,-20000);
39539                 this.el.hide();
39540                 this.collapsedEl.show();
39541                 this.fireEvent("collapsed", this);
39542                 this.fireEvent("invalidated", this);
39543             }
39544         }
39545         
39546     },
39547 */
39548     animateCollapse : function(){
39549         // overridden
39550     },
39551
39552     /**
39553      * Expands this region if it was previously collapsed.
39554      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39555      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39556      */
39557     /*
39558     expand : function(e, skipAnim){
39559         if(e) {
39560             e.stopPropagation();
39561         }
39562         if(!this.collapsed || this.el.hasActiveFx()) {
39563             return;
39564         }
39565         if(this.isSlid){
39566             this.afterSlideIn();
39567             skipAnim = true;
39568         }
39569         this.collapsed = false;
39570         if(this.config.animate && skipAnim !== true){
39571             this.animateExpand();
39572         }else{
39573             this.el.show();
39574             if(this.split){
39575                 this.split.el.show();
39576             }
39577             this.collapsedEl.setLocation(-2000,-2000);
39578             this.collapsedEl.hide();
39579             this.fireEvent("invalidated", this);
39580             this.fireEvent("expanded", this);
39581         }
39582     },
39583 */
39584     animateExpand : function(){
39585         // overridden
39586     },
39587
39588     initTabs : function()
39589     {
39590         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39591         
39592         var ts = new Roo.bootstrap.panel.Tabs({
39593             el: this.bodyEl.dom,
39594             region : this,
39595             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39596             disableTooltips: this.config.disableTabTips,
39597             toolbar : this.config.toolbar
39598         });
39599         
39600         if(this.config.hideTabs){
39601             ts.stripWrap.setDisplayed(false);
39602         }
39603         this.tabs = ts;
39604         ts.resizeTabs = this.config.resizeTabs === true;
39605         ts.minTabWidth = this.config.minTabWidth || 40;
39606         ts.maxTabWidth = this.config.maxTabWidth || 250;
39607         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39608         ts.monitorResize = false;
39609         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39610         ts.bodyEl.addClass('roo-layout-tabs-body');
39611         this.panels.each(this.initPanelAsTab, this);
39612     },
39613
39614     initPanelAsTab : function(panel){
39615         var ti = this.tabs.addTab(
39616             panel.getEl().id,
39617             panel.getTitle(),
39618             null,
39619             this.config.closeOnTab && panel.isClosable(),
39620             panel.tpl
39621         );
39622         if(panel.tabTip !== undefined){
39623             ti.setTooltip(panel.tabTip);
39624         }
39625         ti.on("activate", function(){
39626               this.setActivePanel(panel);
39627         }, this);
39628         
39629         if(this.config.closeOnTab){
39630             ti.on("beforeclose", function(t, e){
39631                 e.cancel = true;
39632                 this.remove(panel);
39633             }, this);
39634         }
39635         
39636         panel.tabItem = ti;
39637         
39638         return ti;
39639     },
39640
39641     updatePanelTitle : function(panel, title)
39642     {
39643         if(this.activePanel == panel){
39644             this.updateTitle(title);
39645         }
39646         if(this.tabs){
39647             var ti = this.tabs.getTab(panel.getEl().id);
39648             ti.setText(title);
39649             if(panel.tabTip !== undefined){
39650                 ti.setTooltip(panel.tabTip);
39651             }
39652         }
39653     },
39654
39655     updateTitle : function(title){
39656         if(this.titleTextEl && !this.config.title){
39657             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39658         }
39659     },
39660
39661     setActivePanel : function(panel)
39662     {
39663         panel = this.getPanel(panel);
39664         if(this.activePanel && this.activePanel != panel){
39665             if(this.activePanel.setActiveState(false) === false){
39666                 return;
39667             }
39668         }
39669         this.activePanel = panel;
39670         panel.setActiveState(true);
39671         if(this.panelSize){
39672             panel.setSize(this.panelSize.width, this.panelSize.height);
39673         }
39674         if(this.closeBtn){
39675             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39676         }
39677         this.updateTitle(panel.getTitle());
39678         if(this.tabs){
39679             this.fireEvent("invalidated", this);
39680         }
39681         this.fireEvent("panelactivated", this, panel);
39682     },
39683
39684     /**
39685      * Shows the specified panel.
39686      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39687      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39688      */
39689     showPanel : function(panel)
39690     {
39691         panel = this.getPanel(panel);
39692         if(panel){
39693             if(this.tabs){
39694                 var tab = this.tabs.getTab(panel.getEl().id);
39695                 if(tab.isHidden()){
39696                     this.tabs.unhideTab(tab.id);
39697                 }
39698                 tab.activate();
39699             }else{
39700                 this.setActivePanel(panel);
39701             }
39702         }
39703         return panel;
39704     },
39705
39706     /**
39707      * Get the active panel for this region.
39708      * @return {Roo.ContentPanel} The active panel or null
39709      */
39710     getActivePanel : function(){
39711         return this.activePanel;
39712     },
39713
39714     validateVisibility : function(){
39715         if(this.panels.getCount() < 1){
39716             this.updateTitle("&#160;");
39717             this.closeBtn.hide();
39718             this.hide();
39719         }else{
39720             if(!this.isVisible()){
39721                 this.show();
39722             }
39723         }
39724     },
39725
39726     /**
39727      * Adds the passed ContentPanel(s) to this region.
39728      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39729      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39730      */
39731     add : function(panel)
39732     {
39733         if(arguments.length > 1){
39734             for(var i = 0, len = arguments.length; i < len; i++) {
39735                 this.add(arguments[i]);
39736             }
39737             return null;
39738         }
39739         
39740         // if we have not been rendered yet, then we can not really do much of this..
39741         if (!this.bodyEl) {
39742             this.unrendered_panels.push(panel);
39743             return panel;
39744         }
39745         
39746         
39747         
39748         
39749         if(this.hasPanel(panel)){
39750             this.showPanel(panel);
39751             return panel;
39752         }
39753         panel.setRegion(this);
39754         this.panels.add(panel);
39755        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39756             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39757             // and hide them... ???
39758             this.bodyEl.dom.appendChild(panel.getEl().dom);
39759             if(panel.background !== true){
39760                 this.setActivePanel(panel);
39761             }
39762             this.fireEvent("paneladded", this, panel);
39763             return panel;
39764         }
39765         */
39766         if(!this.tabs){
39767             this.initTabs();
39768         }else{
39769             this.initPanelAsTab(panel);
39770         }
39771         
39772         
39773         if(panel.background !== true){
39774             this.tabs.activate(panel.getEl().id);
39775         }
39776         this.fireEvent("paneladded", this, panel);
39777         return panel;
39778     },
39779
39780     /**
39781      * Hides the tab for the specified panel.
39782      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39783      */
39784     hidePanel : function(panel){
39785         if(this.tabs && (panel = this.getPanel(panel))){
39786             this.tabs.hideTab(panel.getEl().id);
39787         }
39788     },
39789
39790     /**
39791      * Unhides the tab for a previously hidden panel.
39792      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39793      */
39794     unhidePanel : function(panel){
39795         if(this.tabs && (panel = this.getPanel(panel))){
39796             this.tabs.unhideTab(panel.getEl().id);
39797         }
39798     },
39799
39800     clearPanels : function(){
39801         while(this.panels.getCount() > 0){
39802              this.remove(this.panels.first());
39803         }
39804     },
39805
39806     /**
39807      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39808      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39809      * @param {Boolean} preservePanel Overrides the config preservePanel option
39810      * @return {Roo.ContentPanel} The panel that was removed
39811      */
39812     remove : function(panel, preservePanel)
39813     {
39814         panel = this.getPanel(panel);
39815         if(!panel){
39816             return null;
39817         }
39818         var e = {};
39819         this.fireEvent("beforeremove", this, panel, e);
39820         if(e.cancel === true){
39821             return null;
39822         }
39823         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39824         var panelId = panel.getId();
39825         this.panels.removeKey(panelId);
39826         if(preservePanel){
39827             document.body.appendChild(panel.getEl().dom);
39828         }
39829         if(this.tabs){
39830             this.tabs.removeTab(panel.getEl().id);
39831         }else if (!preservePanel){
39832             this.bodyEl.dom.removeChild(panel.getEl().dom);
39833         }
39834         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39835             var p = this.panels.first();
39836             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39837             tempEl.appendChild(p.getEl().dom);
39838             this.bodyEl.update("");
39839             this.bodyEl.dom.appendChild(p.getEl().dom);
39840             tempEl = null;
39841             this.updateTitle(p.getTitle());
39842             this.tabs = null;
39843             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39844             this.setActivePanel(p);
39845         }
39846         panel.setRegion(null);
39847         if(this.activePanel == panel){
39848             this.activePanel = null;
39849         }
39850         if(this.config.autoDestroy !== false && preservePanel !== true){
39851             try{panel.destroy();}catch(e){}
39852         }
39853         this.fireEvent("panelremoved", this, panel);
39854         return panel;
39855     },
39856
39857     /**
39858      * Returns the TabPanel component used by this region
39859      * @return {Roo.TabPanel}
39860      */
39861     getTabs : function(){
39862         return this.tabs;
39863     },
39864
39865     createTool : function(parentEl, className){
39866         var btn = Roo.DomHelper.append(parentEl, {
39867             tag: "div",
39868             cls: "x-layout-tools-button",
39869             children: [ {
39870                 tag: "div",
39871                 cls: "roo-layout-tools-button-inner " + className,
39872                 html: "&#160;"
39873             }]
39874         }, true);
39875         btn.addClassOnOver("roo-layout-tools-button-over");
39876         return btn;
39877     }
39878 });/*
39879  * Based on:
39880  * Ext JS Library 1.1.1
39881  * Copyright(c) 2006-2007, Ext JS, LLC.
39882  *
39883  * Originally Released Under LGPL - original licence link has changed is not relivant.
39884  *
39885  * Fork - LGPL
39886  * <script type="text/javascript">
39887  */
39888  
39889
39890
39891 /**
39892  * @class Roo.SplitLayoutRegion
39893  * @extends Roo.LayoutRegion
39894  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39895  */
39896 Roo.bootstrap.layout.Split = function(config){
39897     this.cursor = config.cursor;
39898     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39899 };
39900
39901 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39902 {
39903     splitTip : "Drag to resize.",
39904     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39905     useSplitTips : false,
39906
39907     applyConfig : function(config){
39908         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39909     },
39910     
39911     onRender : function(ctr,pos) {
39912         
39913         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39914         if(!this.config.split){
39915             return;
39916         }
39917         if(!this.split){
39918             
39919             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39920                             tag: "div",
39921                             id: this.el.id + "-split",
39922                             cls: "roo-layout-split roo-layout-split-"+this.position,
39923                             html: "&#160;"
39924             });
39925             /** The SplitBar for this region 
39926             * @type Roo.SplitBar */
39927             // does not exist yet...
39928             Roo.log([this.position, this.orientation]);
39929             
39930             this.split = new Roo.bootstrap.SplitBar({
39931                 dragElement : splitEl,
39932                 resizingElement: this.el,
39933                 orientation : this.orientation
39934             });
39935             
39936             this.split.on("moved", this.onSplitMove, this);
39937             this.split.useShim = this.config.useShim === true;
39938             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39939             if(this.useSplitTips){
39940                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39941             }
39942             //if(config.collapsible){
39943             //    this.split.el.on("dblclick", this.collapse,  this);
39944             //}
39945         }
39946         if(typeof this.config.minSize != "undefined"){
39947             this.split.minSize = this.config.minSize;
39948         }
39949         if(typeof this.config.maxSize != "undefined"){
39950             this.split.maxSize = this.config.maxSize;
39951         }
39952         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39953             this.hideSplitter();
39954         }
39955         
39956     },
39957
39958     getHMaxSize : function(){
39959          var cmax = this.config.maxSize || 10000;
39960          var center = this.mgr.getRegion("center");
39961          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39962     },
39963
39964     getVMaxSize : function(){
39965          var cmax = this.config.maxSize || 10000;
39966          var center = this.mgr.getRegion("center");
39967          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39968     },
39969
39970     onSplitMove : function(split, newSize){
39971         this.fireEvent("resized", this, newSize);
39972     },
39973     
39974     /** 
39975      * Returns the {@link Roo.SplitBar} for this region.
39976      * @return {Roo.SplitBar}
39977      */
39978     getSplitBar : function(){
39979         return this.split;
39980     },
39981     
39982     hide : function(){
39983         this.hideSplitter();
39984         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39985     },
39986
39987     hideSplitter : function(){
39988         if(this.split){
39989             this.split.el.setLocation(-2000,-2000);
39990             this.split.el.hide();
39991         }
39992     },
39993
39994     show : function(){
39995         if(this.split){
39996             this.split.el.show();
39997         }
39998         Roo.bootstrap.layout.Split.superclass.show.call(this);
39999     },
40000     
40001     beforeSlide: function(){
40002         if(Roo.isGecko){// firefox overflow auto bug workaround
40003             this.bodyEl.clip();
40004             if(this.tabs) {
40005                 this.tabs.bodyEl.clip();
40006             }
40007             if(this.activePanel){
40008                 this.activePanel.getEl().clip();
40009                 
40010                 if(this.activePanel.beforeSlide){
40011                     this.activePanel.beforeSlide();
40012                 }
40013             }
40014         }
40015     },
40016     
40017     afterSlide : function(){
40018         if(Roo.isGecko){// firefox overflow auto bug workaround
40019             this.bodyEl.unclip();
40020             if(this.tabs) {
40021                 this.tabs.bodyEl.unclip();
40022             }
40023             if(this.activePanel){
40024                 this.activePanel.getEl().unclip();
40025                 if(this.activePanel.afterSlide){
40026                     this.activePanel.afterSlide();
40027                 }
40028             }
40029         }
40030     },
40031
40032     initAutoHide : function(){
40033         if(this.autoHide !== false){
40034             if(!this.autoHideHd){
40035                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40036                 this.autoHideHd = {
40037                     "mouseout": function(e){
40038                         if(!e.within(this.el, true)){
40039                             st.delay(500);
40040                         }
40041                     },
40042                     "mouseover" : function(e){
40043                         st.cancel();
40044                     },
40045                     scope : this
40046                 };
40047             }
40048             this.el.on(this.autoHideHd);
40049         }
40050     },
40051
40052     clearAutoHide : function(){
40053         if(this.autoHide !== false){
40054             this.el.un("mouseout", this.autoHideHd.mouseout);
40055             this.el.un("mouseover", this.autoHideHd.mouseover);
40056         }
40057     },
40058
40059     clearMonitor : function(){
40060         Roo.get(document).un("click", this.slideInIf, this);
40061     },
40062
40063     // these names are backwards but not changed for compat
40064     slideOut : function(){
40065         if(this.isSlid || this.el.hasActiveFx()){
40066             return;
40067         }
40068         this.isSlid = true;
40069         if(this.collapseBtn){
40070             this.collapseBtn.hide();
40071         }
40072         this.closeBtnState = this.closeBtn.getStyle('display');
40073         this.closeBtn.hide();
40074         if(this.stickBtn){
40075             this.stickBtn.show();
40076         }
40077         this.el.show();
40078         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40079         this.beforeSlide();
40080         this.el.setStyle("z-index", 10001);
40081         this.el.slideIn(this.getSlideAnchor(), {
40082             callback: function(){
40083                 this.afterSlide();
40084                 this.initAutoHide();
40085                 Roo.get(document).on("click", this.slideInIf, this);
40086                 this.fireEvent("slideshow", this);
40087             },
40088             scope: this,
40089             block: true
40090         });
40091     },
40092
40093     afterSlideIn : function(){
40094         this.clearAutoHide();
40095         this.isSlid = false;
40096         this.clearMonitor();
40097         this.el.setStyle("z-index", "");
40098         if(this.collapseBtn){
40099             this.collapseBtn.show();
40100         }
40101         this.closeBtn.setStyle('display', this.closeBtnState);
40102         if(this.stickBtn){
40103             this.stickBtn.hide();
40104         }
40105         this.fireEvent("slidehide", this);
40106     },
40107
40108     slideIn : function(cb){
40109         if(!this.isSlid || this.el.hasActiveFx()){
40110             Roo.callback(cb);
40111             return;
40112         }
40113         this.isSlid = false;
40114         this.beforeSlide();
40115         this.el.slideOut(this.getSlideAnchor(), {
40116             callback: function(){
40117                 this.el.setLeftTop(-10000, -10000);
40118                 this.afterSlide();
40119                 this.afterSlideIn();
40120                 Roo.callback(cb);
40121             },
40122             scope: this,
40123             block: true
40124         });
40125     },
40126     
40127     slideInIf : function(e){
40128         if(!e.within(this.el)){
40129             this.slideIn();
40130         }
40131     },
40132
40133     animateCollapse : function(){
40134         this.beforeSlide();
40135         this.el.setStyle("z-index", 20000);
40136         var anchor = this.getSlideAnchor();
40137         this.el.slideOut(anchor, {
40138             callback : function(){
40139                 this.el.setStyle("z-index", "");
40140                 this.collapsedEl.slideIn(anchor, {duration:.3});
40141                 this.afterSlide();
40142                 this.el.setLocation(-10000,-10000);
40143                 this.el.hide();
40144                 this.fireEvent("collapsed", this);
40145             },
40146             scope: this,
40147             block: true
40148         });
40149     },
40150
40151     animateExpand : function(){
40152         this.beforeSlide();
40153         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40154         this.el.setStyle("z-index", 20000);
40155         this.collapsedEl.hide({
40156             duration:.1
40157         });
40158         this.el.slideIn(this.getSlideAnchor(), {
40159             callback : function(){
40160                 this.el.setStyle("z-index", "");
40161                 this.afterSlide();
40162                 if(this.split){
40163                     this.split.el.show();
40164                 }
40165                 this.fireEvent("invalidated", this);
40166                 this.fireEvent("expanded", this);
40167             },
40168             scope: this,
40169             block: true
40170         });
40171     },
40172
40173     anchors : {
40174         "west" : "left",
40175         "east" : "right",
40176         "north" : "top",
40177         "south" : "bottom"
40178     },
40179
40180     sanchors : {
40181         "west" : "l",
40182         "east" : "r",
40183         "north" : "t",
40184         "south" : "b"
40185     },
40186
40187     canchors : {
40188         "west" : "tl-tr",
40189         "east" : "tr-tl",
40190         "north" : "tl-bl",
40191         "south" : "bl-tl"
40192     },
40193
40194     getAnchor : function(){
40195         return this.anchors[this.position];
40196     },
40197
40198     getCollapseAnchor : function(){
40199         return this.canchors[this.position];
40200     },
40201
40202     getSlideAnchor : function(){
40203         return this.sanchors[this.position];
40204     },
40205
40206     getAlignAdj : function(){
40207         var cm = this.cmargins;
40208         switch(this.position){
40209             case "west":
40210                 return [0, 0];
40211             break;
40212             case "east":
40213                 return [0, 0];
40214             break;
40215             case "north":
40216                 return [0, 0];
40217             break;
40218             case "south":
40219                 return [0, 0];
40220             break;
40221         }
40222     },
40223
40224     getExpandAdj : function(){
40225         var c = this.collapsedEl, cm = this.cmargins;
40226         switch(this.position){
40227             case "west":
40228                 return [-(cm.right+c.getWidth()+cm.left), 0];
40229             break;
40230             case "east":
40231                 return [cm.right+c.getWidth()+cm.left, 0];
40232             break;
40233             case "north":
40234                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40235             break;
40236             case "south":
40237                 return [0, cm.top+cm.bottom+c.getHeight()];
40238             break;
40239         }
40240     }
40241 });/*
40242  * Based on:
40243  * Ext JS Library 1.1.1
40244  * Copyright(c) 2006-2007, Ext JS, LLC.
40245  *
40246  * Originally Released Under LGPL - original licence link has changed is not relivant.
40247  *
40248  * Fork - LGPL
40249  * <script type="text/javascript">
40250  */
40251 /*
40252  * These classes are private internal classes
40253  */
40254 Roo.bootstrap.layout.Center = function(config){
40255     config.region = "center";
40256     Roo.bootstrap.layout.Region.call(this, config);
40257     this.visible = true;
40258     this.minWidth = config.minWidth || 20;
40259     this.minHeight = config.minHeight || 20;
40260 };
40261
40262 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40263     hide : function(){
40264         // center panel can't be hidden
40265     },
40266     
40267     show : function(){
40268         // center panel can't be hidden
40269     },
40270     
40271     getMinWidth: function(){
40272         return this.minWidth;
40273     },
40274     
40275     getMinHeight: function(){
40276         return this.minHeight;
40277     }
40278 });
40279
40280
40281
40282
40283  
40284
40285
40286
40287
40288
40289
40290 Roo.bootstrap.layout.North = function(config)
40291 {
40292     config.region = 'north';
40293     config.cursor = 'n-resize';
40294     
40295     Roo.bootstrap.layout.Split.call(this, config);
40296     
40297     
40298     if(this.split){
40299         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40300         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40301         this.split.el.addClass("roo-layout-split-v");
40302     }
40303     //var size = config.initialSize || config.height;
40304     //if(this.el && typeof size != "undefined"){
40305     //    this.el.setHeight(size);
40306     //}
40307 };
40308 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40309 {
40310     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40311      
40312      
40313     onRender : function(ctr, pos)
40314     {
40315         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40316         var size = this.config.initialSize || this.config.height;
40317         if(this.el && typeof size != "undefined"){
40318             this.el.setHeight(size);
40319         }
40320     
40321     },
40322     
40323     getBox : function(){
40324         if(this.collapsed){
40325             return this.collapsedEl.getBox();
40326         }
40327         var box = this.el.getBox();
40328         if(this.split){
40329             box.height += this.split.el.getHeight();
40330         }
40331         return box;
40332     },
40333     
40334     updateBox : function(box){
40335         if(this.split && !this.collapsed){
40336             box.height -= this.split.el.getHeight();
40337             this.split.el.setLeft(box.x);
40338             this.split.el.setTop(box.y+box.height);
40339             this.split.el.setWidth(box.width);
40340         }
40341         if(this.collapsed){
40342             this.updateBody(box.width, null);
40343         }
40344         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40345     }
40346 });
40347
40348
40349
40350
40351
40352 Roo.bootstrap.layout.South = function(config){
40353     config.region = 'south';
40354     config.cursor = 's-resize';
40355     Roo.bootstrap.layout.Split.call(this, config);
40356     if(this.split){
40357         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40358         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40359         this.split.el.addClass("roo-layout-split-v");
40360     }
40361     
40362 };
40363
40364 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40365     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40366     
40367     onRender : function(ctr, pos)
40368     {
40369         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40370         var size = this.config.initialSize || this.config.height;
40371         if(this.el && typeof size != "undefined"){
40372             this.el.setHeight(size);
40373         }
40374     
40375     },
40376     
40377     getBox : function(){
40378         if(this.collapsed){
40379             return this.collapsedEl.getBox();
40380         }
40381         var box = this.el.getBox();
40382         if(this.split){
40383             var sh = this.split.el.getHeight();
40384             box.height += sh;
40385             box.y -= sh;
40386         }
40387         return box;
40388     },
40389     
40390     updateBox : function(box){
40391         if(this.split && !this.collapsed){
40392             var sh = this.split.el.getHeight();
40393             box.height -= sh;
40394             box.y += sh;
40395             this.split.el.setLeft(box.x);
40396             this.split.el.setTop(box.y-sh);
40397             this.split.el.setWidth(box.width);
40398         }
40399         if(this.collapsed){
40400             this.updateBody(box.width, null);
40401         }
40402         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40403     }
40404 });
40405
40406 Roo.bootstrap.layout.East = function(config){
40407     config.region = "east";
40408     config.cursor = "e-resize";
40409     Roo.bootstrap.layout.Split.call(this, config);
40410     if(this.split){
40411         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40412         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40413         this.split.el.addClass("roo-layout-split-h");
40414     }
40415     
40416 };
40417 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40418     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40419     
40420     onRender : function(ctr, pos)
40421     {
40422         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40423         var size = this.config.initialSize || this.config.width;
40424         if(this.el && typeof size != "undefined"){
40425             this.el.setWidth(size);
40426         }
40427     
40428     },
40429     
40430     getBox : function(){
40431         if(this.collapsed){
40432             return this.collapsedEl.getBox();
40433         }
40434         var box = this.el.getBox();
40435         if(this.split){
40436             var sw = this.split.el.getWidth();
40437             box.width += sw;
40438             box.x -= sw;
40439         }
40440         return box;
40441     },
40442
40443     updateBox : function(box){
40444         if(this.split && !this.collapsed){
40445             var sw = this.split.el.getWidth();
40446             box.width -= sw;
40447             this.split.el.setLeft(box.x);
40448             this.split.el.setTop(box.y);
40449             this.split.el.setHeight(box.height);
40450             box.x += sw;
40451         }
40452         if(this.collapsed){
40453             this.updateBody(null, box.height);
40454         }
40455         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40456     }
40457 });
40458
40459 Roo.bootstrap.layout.West = function(config){
40460     config.region = "west";
40461     config.cursor = "w-resize";
40462     
40463     Roo.bootstrap.layout.Split.call(this, config);
40464     if(this.split){
40465         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40466         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40467         this.split.el.addClass("roo-layout-split-h");
40468     }
40469     
40470 };
40471 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40472     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40473     
40474     onRender: function(ctr, pos)
40475     {
40476         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40477         var size = this.config.initialSize || this.config.width;
40478         if(typeof size != "undefined"){
40479             this.el.setWidth(size);
40480         }
40481     },
40482     
40483     getBox : function(){
40484         if(this.collapsed){
40485             return this.collapsedEl.getBox();
40486         }
40487         var box = this.el.getBox();
40488         if (box.width == 0) {
40489             box.width = this.config.width; // kludge?
40490         }
40491         if(this.split){
40492             box.width += this.split.el.getWidth();
40493         }
40494         return box;
40495     },
40496     
40497     updateBox : function(box){
40498         if(this.split && !this.collapsed){
40499             var sw = this.split.el.getWidth();
40500             box.width -= sw;
40501             this.split.el.setLeft(box.x+box.width);
40502             this.split.el.setTop(box.y);
40503             this.split.el.setHeight(box.height);
40504         }
40505         if(this.collapsed){
40506             this.updateBody(null, box.height);
40507         }
40508         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40509     }
40510 });Roo.namespace("Roo.bootstrap.panel");/*
40511  * Based on:
40512  * Ext JS Library 1.1.1
40513  * Copyright(c) 2006-2007, Ext JS, LLC.
40514  *
40515  * Originally Released Under LGPL - original licence link has changed is not relivant.
40516  *
40517  * Fork - LGPL
40518  * <script type="text/javascript">
40519  */
40520 /**
40521  * @class Roo.ContentPanel
40522  * @extends Roo.util.Observable
40523  * A basic ContentPanel element.
40524  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40525  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40526  * @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
40527  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40528  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40529  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40530  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40531  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40532  * @cfg {String} title          The title for this panel
40533  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40534  * @cfg {String} url            Calls {@link #setUrl} with this value
40535  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40536  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40537  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40538  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40539  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40540  * @cfg {Boolean} badges render the badges
40541  * @cfg {String} cls  extra classes to use  
40542  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40543
40544  * @constructor
40545  * Create a new ContentPanel.
40546  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40547  * @param {String/Object} config A string to set only the title or a config object
40548  * @param {String} content (optional) Set the HTML content for this panel
40549  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40550  */
40551 Roo.bootstrap.panel.Content = function( config){
40552     
40553     this.tpl = config.tpl || false;
40554     
40555     var el = config.el;
40556     var content = config.content;
40557
40558     if(config.autoCreate){ // xtype is available if this is called from factory
40559         el = Roo.id();
40560     }
40561     this.el = Roo.get(el);
40562     if(!this.el && config && config.autoCreate){
40563         if(typeof config.autoCreate == "object"){
40564             if(!config.autoCreate.id){
40565                 config.autoCreate.id = config.id||el;
40566             }
40567             this.el = Roo.DomHelper.append(document.body,
40568                         config.autoCreate, true);
40569         }else{
40570             var elcfg =  {
40571                 tag: "div",
40572                 cls: (config.cls || '') +
40573                     (config.background ? ' bg-' + config.background : '') +
40574                     " roo-layout-inactive-content",
40575                 id: config.id||el
40576             };
40577             if (config.iframe) {
40578                 elcfg.cn = [
40579                     {
40580                         tag : 'iframe',
40581                         style : 'border: 0px',
40582                         src : 'about:blank'
40583                     }
40584                 ];
40585             }
40586               
40587             if (config.html) {
40588                 elcfg.html = config.html;
40589                 
40590             }
40591                         
40592             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40593             if (config.iframe) {
40594                 this.iframeEl = this.el.select('iframe',true).first();
40595             }
40596             
40597         }
40598     } 
40599     this.closable = false;
40600     this.loaded = false;
40601     this.active = false;
40602    
40603       
40604     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40605         
40606         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40607         
40608         this.wrapEl = this.el; //this.el.wrap();
40609         var ti = [];
40610         if (config.toolbar.items) {
40611             ti = config.toolbar.items ;
40612             delete config.toolbar.items ;
40613         }
40614         
40615         var nitems = [];
40616         this.toolbar.render(this.wrapEl, 'before');
40617         for(var i =0;i < ti.length;i++) {
40618           //  Roo.log(['add child', items[i]]);
40619             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40620         }
40621         this.toolbar.items = nitems;
40622         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40623         delete config.toolbar;
40624         
40625     }
40626     /*
40627     // xtype created footer. - not sure if will work as we normally have to render first..
40628     if (this.footer && !this.footer.el && this.footer.xtype) {
40629         if (!this.wrapEl) {
40630             this.wrapEl = this.el.wrap();
40631         }
40632     
40633         this.footer.container = this.wrapEl.createChild();
40634          
40635         this.footer = Roo.factory(this.footer, Roo);
40636         
40637     }
40638     */
40639     
40640      if(typeof config == "string"){
40641         this.title = config;
40642     }else{
40643         Roo.apply(this, config);
40644     }
40645     
40646     if(this.resizeEl){
40647         this.resizeEl = Roo.get(this.resizeEl, true);
40648     }else{
40649         this.resizeEl = this.el;
40650     }
40651     // handle view.xtype
40652     
40653  
40654     
40655     
40656     this.addEvents({
40657         /**
40658          * @event activate
40659          * Fires when this panel is activated. 
40660          * @param {Roo.ContentPanel} this
40661          */
40662         "activate" : true,
40663         /**
40664          * @event deactivate
40665          * Fires when this panel is activated. 
40666          * @param {Roo.ContentPanel} this
40667          */
40668         "deactivate" : true,
40669
40670         /**
40671          * @event resize
40672          * Fires when this panel is resized if fitToFrame is true.
40673          * @param {Roo.ContentPanel} this
40674          * @param {Number} width The width after any component adjustments
40675          * @param {Number} height The height after any component adjustments
40676          */
40677         "resize" : true,
40678         
40679          /**
40680          * @event render
40681          * Fires when this tab is created
40682          * @param {Roo.ContentPanel} this
40683          */
40684         "render" : true,
40685         
40686           /**
40687          * @event scroll
40688          * Fires when this content is scrolled
40689          * @param {Roo.ContentPanel} this
40690          * @param {Event} scrollEvent
40691          */
40692         "scroll" : true
40693         
40694         
40695         
40696     });
40697     
40698
40699     
40700     
40701     if(this.autoScroll && !this.iframe){
40702         this.resizeEl.setStyle("overflow", "auto");
40703         this.resizeEl.on('scroll', this.onScroll, this);
40704     } else {
40705         // fix randome scrolling
40706         //this.el.on('scroll', function() {
40707         //    Roo.log('fix random scolling');
40708         //    this.scrollTo('top',0); 
40709         //});
40710     }
40711     content = content || this.content;
40712     if(content){
40713         this.setContent(content);
40714     }
40715     if(config && config.url){
40716         this.setUrl(this.url, this.params, this.loadOnce);
40717     }
40718     
40719     
40720     
40721     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40722     
40723     if (this.view && typeof(this.view.xtype) != 'undefined') {
40724         this.view.el = this.el.appendChild(document.createElement("div"));
40725         this.view = Roo.factory(this.view); 
40726         this.view.render  &&  this.view.render(false, '');  
40727     }
40728     
40729     
40730     this.fireEvent('render', this);
40731 };
40732
40733 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40734     
40735     cls : '',
40736     background : '',
40737     
40738     tabTip : '',
40739     
40740     iframe : false,
40741     iframeEl : false,
40742     
40743     /* Resize Element - use this to work out scroll etc. */
40744     resizeEl : false,
40745     
40746     setRegion : function(region){
40747         this.region = region;
40748         this.setActiveClass(region && !this.background);
40749     },
40750     
40751     
40752     setActiveClass: function(state)
40753     {
40754         if(state){
40755            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40756            this.el.setStyle('position','relative');
40757         }else{
40758            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40759            this.el.setStyle('position', 'absolute');
40760         } 
40761     },
40762     
40763     /**
40764      * Returns the toolbar for this Panel if one was configured. 
40765      * @return {Roo.Toolbar} 
40766      */
40767     getToolbar : function(){
40768         return this.toolbar;
40769     },
40770     
40771     setActiveState : function(active)
40772     {
40773         this.active = active;
40774         this.setActiveClass(active);
40775         if(!active){
40776             if(this.fireEvent("deactivate", this) === false){
40777                 return false;
40778             }
40779             return true;
40780         }
40781         this.fireEvent("activate", this);
40782         return true;
40783     },
40784     /**
40785      * Updates this panel's element (not for iframe)
40786      * @param {String} content The new content
40787      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40788     */
40789     setContent : function(content, loadScripts){
40790         if (this.iframe) {
40791             return;
40792         }
40793         
40794         this.el.update(content, loadScripts);
40795     },
40796
40797     ignoreResize : function(w, h){
40798         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40799             return true;
40800         }else{
40801             this.lastSize = {width: w, height: h};
40802             return false;
40803         }
40804     },
40805     /**
40806      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40807      * @return {Roo.UpdateManager} The UpdateManager
40808      */
40809     getUpdateManager : function(){
40810         if (this.iframe) {
40811             return false;
40812         }
40813         return this.el.getUpdateManager();
40814     },
40815      /**
40816      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40817      * Does not work with IFRAME contents
40818      * @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:
40819 <pre><code>
40820 panel.load({
40821     url: "your-url.php",
40822     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40823     callback: yourFunction,
40824     scope: yourObject, //(optional scope)
40825     discardUrl: false,
40826     nocache: false,
40827     text: "Loading...",
40828     timeout: 30,
40829     scripts: false
40830 });
40831 </code></pre>
40832      
40833      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40834      * 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.
40835      * @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}
40836      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40837      * @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.
40838      * @return {Roo.ContentPanel} this
40839      */
40840     load : function(){
40841         
40842         if (this.iframe) {
40843             return this;
40844         }
40845         
40846         var um = this.el.getUpdateManager();
40847         um.update.apply(um, arguments);
40848         return this;
40849     },
40850
40851
40852     /**
40853      * 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.
40854      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40855      * @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)
40856      * @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)
40857      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40858      */
40859     setUrl : function(url, params, loadOnce){
40860         if (this.iframe) {
40861             this.iframeEl.dom.src = url;
40862             return false;
40863         }
40864         
40865         if(this.refreshDelegate){
40866             this.removeListener("activate", this.refreshDelegate);
40867         }
40868         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40869         this.on("activate", this.refreshDelegate);
40870         return this.el.getUpdateManager();
40871     },
40872     
40873     _handleRefresh : function(url, params, loadOnce){
40874         if(!loadOnce || !this.loaded){
40875             var updater = this.el.getUpdateManager();
40876             updater.update(url, params, this._setLoaded.createDelegate(this));
40877         }
40878     },
40879     
40880     _setLoaded : function(){
40881         this.loaded = true;
40882     }, 
40883     
40884     /**
40885      * Returns this panel's id
40886      * @return {String} 
40887      */
40888     getId : function(){
40889         return this.el.id;
40890     },
40891     
40892     /** 
40893      * Returns this panel's element - used by regiosn to add.
40894      * @return {Roo.Element} 
40895      */
40896     getEl : function(){
40897         return this.wrapEl || this.el;
40898     },
40899     
40900    
40901     
40902     adjustForComponents : function(width, height)
40903     {
40904         //Roo.log('adjustForComponents ');
40905         if(this.resizeEl != this.el){
40906             width -= this.el.getFrameWidth('lr');
40907             height -= this.el.getFrameWidth('tb');
40908         }
40909         if(this.toolbar){
40910             var te = this.toolbar.getEl();
40911             te.setWidth(width);
40912             height -= te.getHeight();
40913         }
40914         if(this.footer){
40915             var te = this.footer.getEl();
40916             te.setWidth(width);
40917             height -= te.getHeight();
40918         }
40919         
40920         
40921         if(this.adjustments){
40922             width += this.adjustments[0];
40923             height += this.adjustments[1];
40924         }
40925         return {"width": width, "height": height};
40926     },
40927     
40928     setSize : function(width, height){
40929         if(this.fitToFrame && !this.ignoreResize(width, height)){
40930             if(this.fitContainer && this.resizeEl != this.el){
40931                 this.el.setSize(width, height);
40932             }
40933             var size = this.adjustForComponents(width, height);
40934             if (this.iframe) {
40935                 this.iframeEl.setSize(width,height);
40936             }
40937             
40938             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40939             this.fireEvent('resize', this, size.width, size.height);
40940             
40941             
40942         }
40943     },
40944     
40945     /**
40946      * Returns this panel's title
40947      * @return {String} 
40948      */
40949     getTitle : function(){
40950         
40951         if (typeof(this.title) != 'object') {
40952             return this.title;
40953         }
40954         
40955         var t = '';
40956         for (var k in this.title) {
40957             if (!this.title.hasOwnProperty(k)) {
40958                 continue;
40959             }
40960             
40961             if (k.indexOf('-') >= 0) {
40962                 var s = k.split('-');
40963                 for (var i = 0; i<s.length; i++) {
40964                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40965                 }
40966             } else {
40967                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40968             }
40969         }
40970         return t;
40971     },
40972     
40973     /**
40974      * Set this panel's title
40975      * @param {String} title
40976      */
40977     setTitle : function(title){
40978         this.title = title;
40979         if(this.region){
40980             this.region.updatePanelTitle(this, title);
40981         }
40982     },
40983     
40984     /**
40985      * Returns true is this panel was configured to be closable
40986      * @return {Boolean} 
40987      */
40988     isClosable : function(){
40989         return this.closable;
40990     },
40991     
40992     beforeSlide : function(){
40993         this.el.clip();
40994         this.resizeEl.clip();
40995     },
40996     
40997     afterSlide : function(){
40998         this.el.unclip();
40999         this.resizeEl.unclip();
41000     },
41001     
41002     /**
41003      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41004      *   Will fail silently if the {@link #setUrl} method has not been called.
41005      *   This does not activate the panel, just updates its content.
41006      */
41007     refresh : function(){
41008         if(this.refreshDelegate){
41009            this.loaded = false;
41010            this.refreshDelegate();
41011         }
41012     },
41013     
41014     /**
41015      * Destroys this panel
41016      */
41017     destroy : function(){
41018         this.el.removeAllListeners();
41019         var tempEl = document.createElement("span");
41020         tempEl.appendChild(this.el.dom);
41021         tempEl.innerHTML = "";
41022         this.el.remove();
41023         this.el = null;
41024     },
41025     
41026     /**
41027      * form - if the content panel contains a form - this is a reference to it.
41028      * @type {Roo.form.Form}
41029      */
41030     form : false,
41031     /**
41032      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41033      *    This contains a reference to it.
41034      * @type {Roo.View}
41035      */
41036     view : false,
41037     
41038       /**
41039      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41040      * <pre><code>
41041
41042 layout.addxtype({
41043        xtype : 'Form',
41044        items: [ .... ]
41045    }
41046 );
41047
41048 </code></pre>
41049      * @param {Object} cfg Xtype definition of item to add.
41050      */
41051     
41052     
41053     getChildContainer: function () {
41054         return this.getEl();
41055     },
41056     
41057     
41058     onScroll : function(e)
41059     {
41060         this.fireEvent('scroll', this, e);
41061     }
41062     
41063     
41064     /*
41065         var  ret = new Roo.factory(cfg);
41066         return ret;
41067         
41068         
41069         // add form..
41070         if (cfg.xtype.match(/^Form$/)) {
41071             
41072             var el;
41073             //if (this.footer) {
41074             //    el = this.footer.container.insertSibling(false, 'before');
41075             //} else {
41076                 el = this.el.createChild();
41077             //}
41078
41079             this.form = new  Roo.form.Form(cfg);
41080             
41081             
41082             if ( this.form.allItems.length) {
41083                 this.form.render(el.dom);
41084             }
41085             return this.form;
41086         }
41087         // should only have one of theses..
41088         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41089             // views.. should not be just added - used named prop 'view''
41090             
41091             cfg.el = this.el.appendChild(document.createElement("div"));
41092             // factory?
41093             
41094             var ret = new Roo.factory(cfg);
41095              
41096              ret.render && ret.render(false, ''); // render blank..
41097             this.view = ret;
41098             return ret;
41099         }
41100         return false;
41101     }
41102     \*/
41103 });
41104  
41105 /**
41106  * @class Roo.bootstrap.panel.Grid
41107  * @extends Roo.bootstrap.panel.Content
41108  * @constructor
41109  * Create a new GridPanel.
41110  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41111  * @param {Object} config A the config object
41112   
41113  */
41114
41115
41116
41117 Roo.bootstrap.panel.Grid = function(config)
41118 {
41119     
41120       
41121     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41122         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41123
41124     config.el = this.wrapper;
41125     //this.el = this.wrapper;
41126     
41127       if (config.container) {
41128         // ctor'ed from a Border/panel.grid
41129         
41130         
41131         this.wrapper.setStyle("overflow", "hidden");
41132         this.wrapper.addClass('roo-grid-container');
41133
41134     }
41135     
41136     
41137     if(config.toolbar){
41138         var tool_el = this.wrapper.createChild();    
41139         this.toolbar = Roo.factory(config.toolbar);
41140         var ti = [];
41141         if (config.toolbar.items) {
41142             ti = config.toolbar.items ;
41143             delete config.toolbar.items ;
41144         }
41145         
41146         var nitems = [];
41147         this.toolbar.render(tool_el);
41148         for(var i =0;i < ti.length;i++) {
41149           //  Roo.log(['add child', items[i]]);
41150             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41151         }
41152         this.toolbar.items = nitems;
41153         
41154         delete config.toolbar;
41155     }
41156     
41157     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41158     config.grid.scrollBody = true;;
41159     config.grid.monitorWindowResize = false; // turn off autosizing
41160     config.grid.autoHeight = false;
41161     config.grid.autoWidth = false;
41162     
41163     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41164     
41165     if (config.background) {
41166         // render grid on panel activation (if panel background)
41167         this.on('activate', function(gp) {
41168             if (!gp.grid.rendered) {
41169                 gp.grid.render(this.wrapper);
41170                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41171             }
41172         });
41173             
41174     } else {
41175         this.grid.render(this.wrapper);
41176         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41177
41178     }
41179     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41180     // ??? needed ??? config.el = this.wrapper;
41181     
41182     
41183     
41184   
41185     // xtype created footer. - not sure if will work as we normally have to render first..
41186     if (this.footer && !this.footer.el && this.footer.xtype) {
41187         
41188         var ctr = this.grid.getView().getFooterPanel(true);
41189         this.footer.dataSource = this.grid.dataSource;
41190         this.footer = Roo.factory(this.footer, Roo);
41191         this.footer.render(ctr);
41192         
41193     }
41194     
41195     
41196     
41197     
41198      
41199 };
41200
41201 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41202     getId : function(){
41203         return this.grid.id;
41204     },
41205     
41206     /**
41207      * Returns the grid for this panel
41208      * @return {Roo.bootstrap.Table} 
41209      */
41210     getGrid : function(){
41211         return this.grid;    
41212     },
41213     
41214     setSize : function(width, height){
41215         if(!this.ignoreResize(width, height)){
41216             var grid = this.grid;
41217             var size = this.adjustForComponents(width, height);
41218             // tfoot is not a footer?
41219           
41220             
41221             var gridel = grid.getGridEl();
41222             gridel.setSize(size.width, size.height);
41223             
41224             var tbd = grid.getGridEl().select('tbody', true).first();
41225             var thd = grid.getGridEl().select('thead',true).first();
41226             var tbf= grid.getGridEl().select('tfoot', true).first();
41227
41228             if (tbf) {
41229                 size.height -= tbf.getHeight();
41230             }
41231             if (thd) {
41232                 size.height -= thd.getHeight();
41233             }
41234             
41235             tbd.setSize(size.width, size.height );
41236             // this is for the account management tab -seems to work there.
41237             var thd = grid.getGridEl().select('thead',true).first();
41238             //if (tbd) {
41239             //    tbd.setSize(size.width, size.height - thd.getHeight());
41240             //}
41241              
41242             grid.autoSize();
41243         }
41244     },
41245      
41246     
41247     
41248     beforeSlide : function(){
41249         this.grid.getView().scroller.clip();
41250     },
41251     
41252     afterSlide : function(){
41253         this.grid.getView().scroller.unclip();
41254     },
41255     
41256     destroy : function(){
41257         this.grid.destroy();
41258         delete this.grid;
41259         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41260     }
41261 });
41262
41263 /**
41264  * @class Roo.bootstrap.panel.Nest
41265  * @extends Roo.bootstrap.panel.Content
41266  * @constructor
41267  * Create a new Panel, that can contain a layout.Border.
41268  * 
41269  * 
41270  * @param {Roo.BorderLayout} layout The layout for this panel
41271  * @param {String/Object} config A string to set only the title or a config object
41272  */
41273 Roo.bootstrap.panel.Nest = function(config)
41274 {
41275     // construct with only one argument..
41276     /* FIXME - implement nicer consturctors
41277     if (layout.layout) {
41278         config = layout;
41279         layout = config.layout;
41280         delete config.layout;
41281     }
41282     if (layout.xtype && !layout.getEl) {
41283         // then layout needs constructing..
41284         layout = Roo.factory(layout, Roo);
41285     }
41286     */
41287     
41288     config.el =  config.layout.getEl();
41289     
41290     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41291     
41292     config.layout.monitorWindowResize = false; // turn off autosizing
41293     this.layout = config.layout;
41294     this.layout.getEl().addClass("roo-layout-nested-layout");
41295     this.layout.parent = this;
41296     
41297     
41298     
41299     
41300 };
41301
41302 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41303
41304     setSize : function(width, height){
41305         if(!this.ignoreResize(width, height)){
41306             var size = this.adjustForComponents(width, height);
41307             var el = this.layout.getEl();
41308             if (size.height < 1) {
41309                 el.setWidth(size.width);   
41310             } else {
41311                 el.setSize(size.width, size.height);
41312             }
41313             var touch = el.dom.offsetWidth;
41314             this.layout.layout();
41315             // ie requires a double layout on the first pass
41316             if(Roo.isIE && !this.initialized){
41317                 this.initialized = true;
41318                 this.layout.layout();
41319             }
41320         }
41321     },
41322     
41323     // activate all subpanels if not currently active..
41324     
41325     setActiveState : function(active){
41326         this.active = active;
41327         this.setActiveClass(active);
41328         
41329         if(!active){
41330             this.fireEvent("deactivate", this);
41331             return;
41332         }
41333         
41334         this.fireEvent("activate", this);
41335         // not sure if this should happen before or after..
41336         if (!this.layout) {
41337             return; // should not happen..
41338         }
41339         var reg = false;
41340         for (var r in this.layout.regions) {
41341             reg = this.layout.getRegion(r);
41342             if (reg.getActivePanel()) {
41343                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41344                 reg.setActivePanel(reg.getActivePanel());
41345                 continue;
41346             }
41347             if (!reg.panels.length) {
41348                 continue;
41349             }
41350             reg.showPanel(reg.getPanel(0));
41351         }
41352         
41353         
41354         
41355         
41356     },
41357     
41358     /**
41359      * Returns the nested BorderLayout for this panel
41360      * @return {Roo.BorderLayout} 
41361      */
41362     getLayout : function(){
41363         return this.layout;
41364     },
41365     
41366      /**
41367      * Adds a xtype elements to the layout of the nested panel
41368      * <pre><code>
41369
41370 panel.addxtype({
41371        xtype : 'ContentPanel',
41372        region: 'west',
41373        items: [ .... ]
41374    }
41375 );
41376
41377 panel.addxtype({
41378         xtype : 'NestedLayoutPanel',
41379         region: 'west',
41380         layout: {
41381            center: { },
41382            west: { }   
41383         },
41384         items : [ ... list of content panels or nested layout panels.. ]
41385    }
41386 );
41387 </code></pre>
41388      * @param {Object} cfg Xtype definition of item to add.
41389      */
41390     addxtype : function(cfg) {
41391         return this.layout.addxtype(cfg);
41392     
41393     }
41394 });/*
41395  * Based on:
41396  * Ext JS Library 1.1.1
41397  * Copyright(c) 2006-2007, Ext JS, LLC.
41398  *
41399  * Originally Released Under LGPL - original licence link has changed is not relivant.
41400  *
41401  * Fork - LGPL
41402  * <script type="text/javascript">
41403  */
41404 /**
41405  * @class Roo.TabPanel
41406  * @extends Roo.util.Observable
41407  * A lightweight tab container.
41408  * <br><br>
41409  * Usage:
41410  * <pre><code>
41411 // basic tabs 1, built from existing content
41412 var tabs = new Roo.TabPanel("tabs1");
41413 tabs.addTab("script", "View Script");
41414 tabs.addTab("markup", "View Markup");
41415 tabs.activate("script");
41416
41417 // more advanced tabs, built from javascript
41418 var jtabs = new Roo.TabPanel("jtabs");
41419 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41420
41421 // set up the UpdateManager
41422 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41423 var updater = tab2.getUpdateManager();
41424 updater.setDefaultUrl("ajax1.htm");
41425 tab2.on('activate', updater.refresh, updater, true);
41426
41427 // Use setUrl for Ajax loading
41428 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41429 tab3.setUrl("ajax2.htm", null, true);
41430
41431 // Disabled tab
41432 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41433 tab4.disable();
41434
41435 jtabs.activate("jtabs-1");
41436  * </code></pre>
41437  * @constructor
41438  * Create a new TabPanel.
41439  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41440  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41441  */
41442 Roo.bootstrap.panel.Tabs = function(config){
41443     /**
41444     * The container element for this TabPanel.
41445     * @type Roo.Element
41446     */
41447     this.el = Roo.get(config.el);
41448     delete config.el;
41449     if(config){
41450         if(typeof config == "boolean"){
41451             this.tabPosition = config ? "bottom" : "top";
41452         }else{
41453             Roo.apply(this, config);
41454         }
41455     }
41456     
41457     if(this.tabPosition == "bottom"){
41458         // if tabs are at the bottom = create the body first.
41459         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41460         this.el.addClass("roo-tabs-bottom");
41461     }
41462     // next create the tabs holders
41463     
41464     if (this.tabPosition == "west"){
41465         
41466         var reg = this.region; // fake it..
41467         while (reg) {
41468             if (!reg.mgr.parent) {
41469                 break;
41470             }
41471             reg = reg.mgr.parent.region;
41472         }
41473         Roo.log("got nest?");
41474         Roo.log(reg);
41475         if (reg.mgr.getRegion('west')) {
41476             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41477             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41478             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41479             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41480             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41481         
41482             
41483         }
41484         
41485         
41486     } else {
41487      
41488         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41489         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41490         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41491         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41492     }
41493     
41494     
41495     if(Roo.isIE){
41496         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41497     }
41498     
41499     // finally - if tabs are at the top, then create the body last..
41500     if(this.tabPosition != "bottom"){
41501         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41502          * @type Roo.Element
41503          */
41504         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41505         this.el.addClass("roo-tabs-top");
41506     }
41507     this.items = [];
41508
41509     this.bodyEl.setStyle("position", "relative");
41510
41511     this.active = null;
41512     this.activateDelegate = this.activate.createDelegate(this);
41513
41514     this.addEvents({
41515         /**
41516          * @event tabchange
41517          * Fires when the active tab changes
41518          * @param {Roo.TabPanel} this
41519          * @param {Roo.TabPanelItem} activePanel The new active tab
41520          */
41521         "tabchange": true,
41522         /**
41523          * @event beforetabchange
41524          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41525          * @param {Roo.TabPanel} this
41526          * @param {Object} e Set cancel to true on this object to cancel the tab change
41527          * @param {Roo.TabPanelItem} tab The tab being changed to
41528          */
41529         "beforetabchange" : true
41530     });
41531
41532     Roo.EventManager.onWindowResize(this.onResize, this);
41533     this.cpad = this.el.getPadding("lr");
41534     this.hiddenCount = 0;
41535
41536
41537     // toolbar on the tabbar support...
41538     if (this.toolbar) {
41539         alert("no toolbar support yet");
41540         this.toolbar  = false;
41541         /*
41542         var tcfg = this.toolbar;
41543         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41544         this.toolbar = new Roo.Toolbar(tcfg);
41545         if (Roo.isSafari) {
41546             var tbl = tcfg.container.child('table', true);
41547             tbl.setAttribute('width', '100%');
41548         }
41549         */
41550         
41551     }
41552    
41553
41554
41555     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41556 };
41557
41558 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41559     /*
41560      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41561      */
41562     tabPosition : "top",
41563     /*
41564      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41565      */
41566     currentTabWidth : 0,
41567     /*
41568      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41569      */
41570     minTabWidth : 40,
41571     /*
41572      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41573      */
41574     maxTabWidth : 250,
41575     /*
41576      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41577      */
41578     preferredTabWidth : 175,
41579     /*
41580      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41581      */
41582     resizeTabs : false,
41583     /*
41584      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41585      */
41586     monitorResize : true,
41587     /*
41588      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41589      */
41590     toolbar : false,  // set by caller..
41591     
41592     region : false, /// set by caller
41593     
41594     disableTooltips : true, // not used yet...
41595
41596     /**
41597      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41598      * @param {String} id The id of the div to use <b>or create</b>
41599      * @param {String} text The text for the tab
41600      * @param {String} content (optional) Content to put in the TabPanelItem body
41601      * @param {Boolean} closable (optional) True to create a close icon on the tab
41602      * @return {Roo.TabPanelItem} The created TabPanelItem
41603      */
41604     addTab : function(id, text, content, closable, tpl)
41605     {
41606         var item = new Roo.bootstrap.panel.TabItem({
41607             panel: this,
41608             id : id,
41609             text : text,
41610             closable : closable,
41611             tpl : tpl
41612         });
41613         this.addTabItem(item);
41614         if(content){
41615             item.setContent(content);
41616         }
41617         return item;
41618     },
41619
41620     /**
41621      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41622      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41623      * @return {Roo.TabPanelItem}
41624      */
41625     getTab : function(id){
41626         return this.items[id];
41627     },
41628
41629     /**
41630      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41631      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41632      */
41633     hideTab : function(id){
41634         var t = this.items[id];
41635         if(!t.isHidden()){
41636            t.setHidden(true);
41637            this.hiddenCount++;
41638            this.autoSizeTabs();
41639         }
41640     },
41641
41642     /**
41643      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41644      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41645      */
41646     unhideTab : function(id){
41647         var t = this.items[id];
41648         if(t.isHidden()){
41649            t.setHidden(false);
41650            this.hiddenCount--;
41651            this.autoSizeTabs();
41652         }
41653     },
41654
41655     /**
41656      * Adds an existing {@link Roo.TabPanelItem}.
41657      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41658      */
41659     addTabItem : function(item)
41660     {
41661         this.items[item.id] = item;
41662         this.items.push(item);
41663         this.autoSizeTabs();
41664       //  if(this.resizeTabs){
41665     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41666   //         this.autoSizeTabs();
41667 //        }else{
41668 //            item.autoSize();
41669        // }
41670     },
41671
41672     /**
41673      * Removes a {@link Roo.TabPanelItem}.
41674      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41675      */
41676     removeTab : function(id){
41677         var items = this.items;
41678         var tab = items[id];
41679         if(!tab) { return; }
41680         var index = items.indexOf(tab);
41681         if(this.active == tab && items.length > 1){
41682             var newTab = this.getNextAvailable(index);
41683             if(newTab) {
41684                 newTab.activate();
41685             }
41686         }
41687         this.stripEl.dom.removeChild(tab.pnode.dom);
41688         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41689             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41690         }
41691         items.splice(index, 1);
41692         delete this.items[tab.id];
41693         tab.fireEvent("close", tab);
41694         tab.purgeListeners();
41695         this.autoSizeTabs();
41696     },
41697
41698     getNextAvailable : function(start){
41699         var items = this.items;
41700         var index = start;
41701         // look for a next tab that will slide over to
41702         // replace the one being removed
41703         while(index < items.length){
41704             var item = items[++index];
41705             if(item && !item.isHidden()){
41706                 return item;
41707             }
41708         }
41709         // if one isn't found select the previous tab (on the left)
41710         index = start;
41711         while(index >= 0){
41712             var item = items[--index];
41713             if(item && !item.isHidden()){
41714                 return item;
41715             }
41716         }
41717         return null;
41718     },
41719
41720     /**
41721      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41722      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41723      */
41724     disableTab : function(id){
41725         var tab = this.items[id];
41726         if(tab && this.active != tab){
41727             tab.disable();
41728         }
41729     },
41730
41731     /**
41732      * Enables a {@link Roo.TabPanelItem} that is disabled.
41733      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41734      */
41735     enableTab : function(id){
41736         var tab = this.items[id];
41737         tab.enable();
41738     },
41739
41740     /**
41741      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41742      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41743      * @return {Roo.TabPanelItem} The TabPanelItem.
41744      */
41745     activate : function(id)
41746     {
41747         //Roo.log('activite:'  + id);
41748         
41749         var tab = this.items[id];
41750         if(!tab){
41751             return null;
41752         }
41753         if(tab == this.active || tab.disabled){
41754             return tab;
41755         }
41756         var e = {};
41757         this.fireEvent("beforetabchange", this, e, tab);
41758         if(e.cancel !== true && !tab.disabled){
41759             if(this.active){
41760                 this.active.hide();
41761             }
41762             this.active = this.items[id];
41763             this.active.show();
41764             this.fireEvent("tabchange", this, this.active);
41765         }
41766         return tab;
41767     },
41768
41769     /**
41770      * Gets the active {@link Roo.TabPanelItem}.
41771      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41772      */
41773     getActiveTab : function(){
41774         return this.active;
41775     },
41776
41777     /**
41778      * Updates the tab body element to fit the height of the container element
41779      * for overflow scrolling
41780      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41781      */
41782     syncHeight : function(targetHeight){
41783         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41784         var bm = this.bodyEl.getMargins();
41785         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41786         this.bodyEl.setHeight(newHeight);
41787         return newHeight;
41788     },
41789
41790     onResize : function(){
41791         if(this.monitorResize){
41792             this.autoSizeTabs();
41793         }
41794     },
41795
41796     /**
41797      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41798      */
41799     beginUpdate : function(){
41800         this.updating = true;
41801     },
41802
41803     /**
41804      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41805      */
41806     endUpdate : function(){
41807         this.updating = false;
41808         this.autoSizeTabs();
41809     },
41810
41811     /**
41812      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41813      */
41814     autoSizeTabs : function()
41815     {
41816         var count = this.items.length;
41817         var vcount = count - this.hiddenCount;
41818         
41819         if (vcount < 2) {
41820             this.stripEl.hide();
41821         } else {
41822             this.stripEl.show();
41823         }
41824         
41825         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41826             return;
41827         }
41828         
41829         
41830         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41831         var availWidth = Math.floor(w / vcount);
41832         var b = this.stripBody;
41833         if(b.getWidth() > w){
41834             var tabs = this.items;
41835             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41836             if(availWidth < this.minTabWidth){
41837                 /*if(!this.sleft){    // incomplete scrolling code
41838                     this.createScrollButtons();
41839                 }
41840                 this.showScroll();
41841                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41842             }
41843         }else{
41844             if(this.currentTabWidth < this.preferredTabWidth){
41845                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41846             }
41847         }
41848     },
41849
41850     /**
41851      * Returns the number of tabs in this TabPanel.
41852      * @return {Number}
41853      */
41854      getCount : function(){
41855          return this.items.length;
41856      },
41857
41858     /**
41859      * Resizes all the tabs to the passed width
41860      * @param {Number} The new width
41861      */
41862     setTabWidth : function(width){
41863         this.currentTabWidth = width;
41864         for(var i = 0, len = this.items.length; i < len; i++) {
41865                 if(!this.items[i].isHidden()) {
41866                 this.items[i].setWidth(width);
41867             }
41868         }
41869     },
41870
41871     /**
41872      * Destroys this TabPanel
41873      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41874      */
41875     destroy : function(removeEl){
41876         Roo.EventManager.removeResizeListener(this.onResize, this);
41877         for(var i = 0, len = this.items.length; i < len; i++){
41878             this.items[i].purgeListeners();
41879         }
41880         if(removeEl === true){
41881             this.el.update("");
41882             this.el.remove();
41883         }
41884     },
41885     
41886     createStrip : function(container)
41887     {
41888         var strip = document.createElement("nav");
41889         strip.className = Roo.bootstrap.version == 4 ?
41890             "navbar-light bg-light" : 
41891             "navbar navbar-default"; //"x-tabs-wrap";
41892         container.appendChild(strip);
41893         return strip;
41894     },
41895     
41896     createStripList : function(strip)
41897     {
41898         // div wrapper for retard IE
41899         // returns the "tr" element.
41900         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41901         //'<div class="x-tabs-strip-wrap">'+
41902           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41903           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41904         return strip.firstChild; //.firstChild.firstChild.firstChild;
41905     },
41906     createBody : function(container)
41907     {
41908         var body = document.createElement("div");
41909         Roo.id(body, "tab-body");
41910         //Roo.fly(body).addClass("x-tabs-body");
41911         Roo.fly(body).addClass("tab-content");
41912         container.appendChild(body);
41913         return body;
41914     },
41915     createItemBody :function(bodyEl, id){
41916         var body = Roo.getDom(id);
41917         if(!body){
41918             body = document.createElement("div");
41919             body.id = id;
41920         }
41921         //Roo.fly(body).addClass("x-tabs-item-body");
41922         Roo.fly(body).addClass("tab-pane");
41923          bodyEl.insertBefore(body, bodyEl.firstChild);
41924         return body;
41925     },
41926     /** @private */
41927     createStripElements :  function(stripEl, text, closable, tpl)
41928     {
41929         var td = document.createElement("li"); // was td..
41930         td.className = 'nav-item';
41931         
41932         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41933         
41934         
41935         stripEl.appendChild(td);
41936         /*if(closable){
41937             td.className = "x-tabs-closable";
41938             if(!this.closeTpl){
41939                 this.closeTpl = new Roo.Template(
41940                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41941                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41942                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41943                 );
41944             }
41945             var el = this.closeTpl.overwrite(td, {"text": text});
41946             var close = el.getElementsByTagName("div")[0];
41947             var inner = el.getElementsByTagName("em")[0];
41948             return {"el": el, "close": close, "inner": inner};
41949         } else {
41950         */
41951         // not sure what this is..
41952 //            if(!this.tabTpl){
41953                 //this.tabTpl = new Roo.Template(
41954                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41955                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41956                 //);
41957 //                this.tabTpl = new Roo.Template(
41958 //                   '<a href="#">' +
41959 //                   '<span unselectable="on"' +
41960 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41961 //                            ' >{text}</span></a>'
41962 //                );
41963 //                
41964 //            }
41965
41966
41967             var template = tpl || this.tabTpl || false;
41968             
41969             if(!template){
41970                 template =  new Roo.Template(
41971                         Roo.bootstrap.version == 4 ? 
41972                             (
41973                                 '<a class="nav-link" href="#" unselectable="on"' +
41974                                      (this.disableTooltips ? '' : ' title="{text}"') +
41975                                      ' >{text}</a>'
41976                             ) : (
41977                                 '<a class="nav-link" href="#">' +
41978                                 '<span unselectable="on"' +
41979                                          (this.disableTooltips ? '' : ' title="{text}"') +
41980                                     ' >{text}</span></a>'
41981                             )
41982                 );
41983             }
41984             
41985             switch (typeof(template)) {
41986                 case 'object' :
41987                     break;
41988                 case 'string' :
41989                     template = new Roo.Template(template);
41990                     break;
41991                 default :
41992                     break;
41993             }
41994             
41995             var el = template.overwrite(td, {"text": text});
41996             
41997             var inner = el.getElementsByTagName("span")[0];
41998             
41999             return {"el": el, "inner": inner};
42000             
42001     }
42002         
42003     
42004 });
42005
42006 /**
42007  * @class Roo.TabPanelItem
42008  * @extends Roo.util.Observable
42009  * Represents an individual item (tab plus body) in a TabPanel.
42010  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42011  * @param {String} id The id of this TabPanelItem
42012  * @param {String} text The text for the tab of this TabPanelItem
42013  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42014  */
42015 Roo.bootstrap.panel.TabItem = function(config){
42016     /**
42017      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42018      * @type Roo.TabPanel
42019      */
42020     this.tabPanel = config.panel;
42021     /**
42022      * The id for this TabPanelItem
42023      * @type String
42024      */
42025     this.id = config.id;
42026     /** @private */
42027     this.disabled = false;
42028     /** @private */
42029     this.text = config.text;
42030     /** @private */
42031     this.loaded = false;
42032     this.closable = config.closable;
42033
42034     /**
42035      * The body element for this TabPanelItem.
42036      * @type Roo.Element
42037      */
42038     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42039     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42040     this.bodyEl.setStyle("display", "block");
42041     this.bodyEl.setStyle("zoom", "1");
42042     //this.hideAction();
42043
42044     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42045     /** @private */
42046     this.el = Roo.get(els.el);
42047     this.inner = Roo.get(els.inner, true);
42048      this.textEl = Roo.bootstrap.version == 4 ?
42049         this.el : Roo.get(this.el.dom.firstChild, true);
42050
42051     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42052     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42053
42054     
42055 //    this.el.on("mousedown", this.onTabMouseDown, this);
42056     this.el.on("click", this.onTabClick, this);
42057     /** @private */
42058     if(config.closable){
42059         var c = Roo.get(els.close, true);
42060         c.dom.title = this.closeText;
42061         c.addClassOnOver("close-over");
42062         c.on("click", this.closeClick, this);
42063      }
42064
42065     this.addEvents({
42066          /**
42067          * @event activate
42068          * Fires when this tab becomes the active tab.
42069          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42070          * @param {Roo.TabPanelItem} this
42071          */
42072         "activate": true,
42073         /**
42074          * @event beforeclose
42075          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42076          * @param {Roo.TabPanelItem} this
42077          * @param {Object} e Set cancel to true on this object to cancel the close.
42078          */
42079         "beforeclose": true,
42080         /**
42081          * @event close
42082          * Fires when this tab is closed.
42083          * @param {Roo.TabPanelItem} this
42084          */
42085          "close": true,
42086         /**
42087          * @event deactivate
42088          * Fires when this tab is no longer the active tab.
42089          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42090          * @param {Roo.TabPanelItem} this
42091          */
42092          "deactivate" : true
42093     });
42094     this.hidden = false;
42095
42096     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42097 };
42098
42099 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42100            {
42101     purgeListeners : function(){
42102        Roo.util.Observable.prototype.purgeListeners.call(this);
42103        this.el.removeAllListeners();
42104     },
42105     /**
42106      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42107      */
42108     show : function(){
42109         this.status_node.addClass("active");
42110         this.showAction();
42111         if(Roo.isOpera){
42112             this.tabPanel.stripWrap.repaint();
42113         }
42114         this.fireEvent("activate", this.tabPanel, this);
42115     },
42116
42117     /**
42118      * Returns true if this tab is the active tab.
42119      * @return {Boolean}
42120      */
42121     isActive : function(){
42122         return this.tabPanel.getActiveTab() == this;
42123     },
42124
42125     /**
42126      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42127      */
42128     hide : function(){
42129         this.status_node.removeClass("active");
42130         this.hideAction();
42131         this.fireEvent("deactivate", this.tabPanel, this);
42132     },
42133
42134     hideAction : function(){
42135         this.bodyEl.hide();
42136         this.bodyEl.setStyle("position", "absolute");
42137         this.bodyEl.setLeft("-20000px");
42138         this.bodyEl.setTop("-20000px");
42139     },
42140
42141     showAction : function(){
42142         this.bodyEl.setStyle("position", "relative");
42143         this.bodyEl.setTop("");
42144         this.bodyEl.setLeft("");
42145         this.bodyEl.show();
42146     },
42147
42148     /**
42149      * Set the tooltip for the tab.
42150      * @param {String} tooltip The tab's tooltip
42151      */
42152     setTooltip : function(text){
42153         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42154             this.textEl.dom.qtip = text;
42155             this.textEl.dom.removeAttribute('title');
42156         }else{
42157             this.textEl.dom.title = text;
42158         }
42159     },
42160
42161     onTabClick : function(e){
42162         e.preventDefault();
42163         this.tabPanel.activate(this.id);
42164     },
42165
42166     onTabMouseDown : function(e){
42167         e.preventDefault();
42168         this.tabPanel.activate(this.id);
42169     },
42170 /*
42171     getWidth : function(){
42172         return this.inner.getWidth();
42173     },
42174
42175     setWidth : function(width){
42176         var iwidth = width - this.linode.getPadding("lr");
42177         this.inner.setWidth(iwidth);
42178         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42179         this.linode.setWidth(width);
42180     },
42181 */
42182     /**
42183      * Show or hide the tab
42184      * @param {Boolean} hidden True to hide or false to show.
42185      */
42186     setHidden : function(hidden){
42187         this.hidden = hidden;
42188         this.linode.setStyle("display", hidden ? "none" : "");
42189     },
42190
42191     /**
42192      * Returns true if this tab is "hidden"
42193      * @return {Boolean}
42194      */
42195     isHidden : function(){
42196         return this.hidden;
42197     },
42198
42199     /**
42200      * Returns the text for this tab
42201      * @return {String}
42202      */
42203     getText : function(){
42204         return this.text;
42205     },
42206     /*
42207     autoSize : function(){
42208         //this.el.beginMeasure();
42209         this.textEl.setWidth(1);
42210         /*
42211          *  #2804 [new] Tabs in Roojs
42212          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42213          */
42214         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42215         //this.el.endMeasure();
42216     //},
42217
42218     /**
42219      * Sets the text for the tab (Note: this also sets the tooltip text)
42220      * @param {String} text The tab's text and tooltip
42221      */
42222     setText : function(text){
42223         this.text = text;
42224         this.textEl.update(text);
42225         this.setTooltip(text);
42226         //if(!this.tabPanel.resizeTabs){
42227         //    this.autoSize();
42228         //}
42229     },
42230     /**
42231      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42232      */
42233     activate : function(){
42234         this.tabPanel.activate(this.id);
42235     },
42236
42237     /**
42238      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42239      */
42240     disable : function(){
42241         if(this.tabPanel.active != this){
42242             this.disabled = true;
42243             this.status_node.addClass("disabled");
42244         }
42245     },
42246
42247     /**
42248      * Enables this TabPanelItem if it was previously disabled.
42249      */
42250     enable : function(){
42251         this.disabled = false;
42252         this.status_node.removeClass("disabled");
42253     },
42254
42255     /**
42256      * Sets the content for this TabPanelItem.
42257      * @param {String} content The content
42258      * @param {Boolean} loadScripts true to look for and load scripts
42259      */
42260     setContent : function(content, loadScripts){
42261         this.bodyEl.update(content, loadScripts);
42262     },
42263
42264     /**
42265      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42266      * @return {Roo.UpdateManager} The UpdateManager
42267      */
42268     getUpdateManager : function(){
42269         return this.bodyEl.getUpdateManager();
42270     },
42271
42272     /**
42273      * Set a URL to be used to load the content for this TabPanelItem.
42274      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42275      * @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)
42276      * @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)
42277      * @return {Roo.UpdateManager} The UpdateManager
42278      */
42279     setUrl : function(url, params, loadOnce){
42280         if(this.refreshDelegate){
42281             this.un('activate', this.refreshDelegate);
42282         }
42283         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42284         this.on("activate", this.refreshDelegate);
42285         return this.bodyEl.getUpdateManager();
42286     },
42287
42288     /** @private */
42289     _handleRefresh : function(url, params, loadOnce){
42290         if(!loadOnce || !this.loaded){
42291             var updater = this.bodyEl.getUpdateManager();
42292             updater.update(url, params, this._setLoaded.createDelegate(this));
42293         }
42294     },
42295
42296     /**
42297      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42298      *   Will fail silently if the setUrl method has not been called.
42299      *   This does not activate the panel, just updates its content.
42300      */
42301     refresh : function(){
42302         if(this.refreshDelegate){
42303            this.loaded = false;
42304            this.refreshDelegate();
42305         }
42306     },
42307
42308     /** @private */
42309     _setLoaded : function(){
42310         this.loaded = true;
42311     },
42312
42313     /** @private */
42314     closeClick : function(e){
42315         var o = {};
42316         e.stopEvent();
42317         this.fireEvent("beforeclose", this, o);
42318         if(o.cancel !== true){
42319             this.tabPanel.removeTab(this.id);
42320         }
42321     },
42322     /**
42323      * The text displayed in the tooltip for the close icon.
42324      * @type String
42325      */
42326     closeText : "Close this tab"
42327 });
42328 /**
42329 *    This script refer to:
42330 *    Title: International Telephone Input
42331 *    Author: Jack O'Connor
42332 *    Code version:  v12.1.12
42333 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42334 **/
42335
42336 Roo.bootstrap.PhoneInputData = function() {
42337     var d = [
42338       [
42339         "Afghanistan (‫افغانستان‬‎)",
42340         "af",
42341         "93"
42342       ],
42343       [
42344         "Albania (Shqipëri)",
42345         "al",
42346         "355"
42347       ],
42348       [
42349         "Algeria (‫الجزائر‬‎)",
42350         "dz",
42351         "213"
42352       ],
42353       [
42354         "American Samoa",
42355         "as",
42356         "1684"
42357       ],
42358       [
42359         "Andorra",
42360         "ad",
42361         "376"
42362       ],
42363       [
42364         "Angola",
42365         "ao",
42366         "244"
42367       ],
42368       [
42369         "Anguilla",
42370         "ai",
42371         "1264"
42372       ],
42373       [
42374         "Antigua and Barbuda",
42375         "ag",
42376         "1268"
42377       ],
42378       [
42379         "Argentina",
42380         "ar",
42381         "54"
42382       ],
42383       [
42384         "Armenia (Հայաստան)",
42385         "am",
42386         "374"
42387       ],
42388       [
42389         "Aruba",
42390         "aw",
42391         "297"
42392       ],
42393       [
42394         "Australia",
42395         "au",
42396         "61",
42397         0
42398       ],
42399       [
42400         "Austria (Österreich)",
42401         "at",
42402         "43"
42403       ],
42404       [
42405         "Azerbaijan (Azərbaycan)",
42406         "az",
42407         "994"
42408       ],
42409       [
42410         "Bahamas",
42411         "bs",
42412         "1242"
42413       ],
42414       [
42415         "Bahrain (‫البحرين‬‎)",
42416         "bh",
42417         "973"
42418       ],
42419       [
42420         "Bangladesh (বাংলাদেশ)",
42421         "bd",
42422         "880"
42423       ],
42424       [
42425         "Barbados",
42426         "bb",
42427         "1246"
42428       ],
42429       [
42430         "Belarus (Беларусь)",
42431         "by",
42432         "375"
42433       ],
42434       [
42435         "Belgium (België)",
42436         "be",
42437         "32"
42438       ],
42439       [
42440         "Belize",
42441         "bz",
42442         "501"
42443       ],
42444       [
42445         "Benin (Bénin)",
42446         "bj",
42447         "229"
42448       ],
42449       [
42450         "Bermuda",
42451         "bm",
42452         "1441"
42453       ],
42454       [
42455         "Bhutan (འབྲུག)",
42456         "bt",
42457         "975"
42458       ],
42459       [
42460         "Bolivia",
42461         "bo",
42462         "591"
42463       ],
42464       [
42465         "Bosnia and Herzegovina (Босна и Херцеговина)",
42466         "ba",
42467         "387"
42468       ],
42469       [
42470         "Botswana",
42471         "bw",
42472         "267"
42473       ],
42474       [
42475         "Brazil (Brasil)",
42476         "br",
42477         "55"
42478       ],
42479       [
42480         "British Indian Ocean Territory",
42481         "io",
42482         "246"
42483       ],
42484       [
42485         "British Virgin Islands",
42486         "vg",
42487         "1284"
42488       ],
42489       [
42490         "Brunei",
42491         "bn",
42492         "673"
42493       ],
42494       [
42495         "Bulgaria (България)",
42496         "bg",
42497         "359"
42498       ],
42499       [
42500         "Burkina Faso",
42501         "bf",
42502         "226"
42503       ],
42504       [
42505         "Burundi (Uburundi)",
42506         "bi",
42507         "257"
42508       ],
42509       [
42510         "Cambodia (កម្ពុជា)",
42511         "kh",
42512         "855"
42513       ],
42514       [
42515         "Cameroon (Cameroun)",
42516         "cm",
42517         "237"
42518       ],
42519       [
42520         "Canada",
42521         "ca",
42522         "1",
42523         1,
42524         ["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"]
42525       ],
42526       [
42527         "Cape Verde (Kabu Verdi)",
42528         "cv",
42529         "238"
42530       ],
42531       [
42532         "Caribbean Netherlands",
42533         "bq",
42534         "599",
42535         1
42536       ],
42537       [
42538         "Cayman Islands",
42539         "ky",
42540         "1345"
42541       ],
42542       [
42543         "Central African Republic (République centrafricaine)",
42544         "cf",
42545         "236"
42546       ],
42547       [
42548         "Chad (Tchad)",
42549         "td",
42550         "235"
42551       ],
42552       [
42553         "Chile",
42554         "cl",
42555         "56"
42556       ],
42557       [
42558         "China (中国)",
42559         "cn",
42560         "86"
42561       ],
42562       [
42563         "Christmas Island",
42564         "cx",
42565         "61",
42566         2
42567       ],
42568       [
42569         "Cocos (Keeling) Islands",
42570         "cc",
42571         "61",
42572         1
42573       ],
42574       [
42575         "Colombia",
42576         "co",
42577         "57"
42578       ],
42579       [
42580         "Comoros (‫جزر القمر‬‎)",
42581         "km",
42582         "269"
42583       ],
42584       [
42585         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42586         "cd",
42587         "243"
42588       ],
42589       [
42590         "Congo (Republic) (Congo-Brazzaville)",
42591         "cg",
42592         "242"
42593       ],
42594       [
42595         "Cook Islands",
42596         "ck",
42597         "682"
42598       ],
42599       [
42600         "Costa Rica",
42601         "cr",
42602         "506"
42603       ],
42604       [
42605         "Côte d’Ivoire",
42606         "ci",
42607         "225"
42608       ],
42609       [
42610         "Croatia (Hrvatska)",
42611         "hr",
42612         "385"
42613       ],
42614       [
42615         "Cuba",
42616         "cu",
42617         "53"
42618       ],
42619       [
42620         "Curaçao",
42621         "cw",
42622         "599",
42623         0
42624       ],
42625       [
42626         "Cyprus (Κύπρος)",
42627         "cy",
42628         "357"
42629       ],
42630       [
42631         "Czech Republic (Česká republika)",
42632         "cz",
42633         "420"
42634       ],
42635       [
42636         "Denmark (Danmark)",
42637         "dk",
42638         "45"
42639       ],
42640       [
42641         "Djibouti",
42642         "dj",
42643         "253"
42644       ],
42645       [
42646         "Dominica",
42647         "dm",
42648         "1767"
42649       ],
42650       [
42651         "Dominican Republic (República Dominicana)",
42652         "do",
42653         "1",
42654         2,
42655         ["809", "829", "849"]
42656       ],
42657       [
42658         "Ecuador",
42659         "ec",
42660         "593"
42661       ],
42662       [
42663         "Egypt (‫مصر‬‎)",
42664         "eg",
42665         "20"
42666       ],
42667       [
42668         "El Salvador",
42669         "sv",
42670         "503"
42671       ],
42672       [
42673         "Equatorial Guinea (Guinea Ecuatorial)",
42674         "gq",
42675         "240"
42676       ],
42677       [
42678         "Eritrea",
42679         "er",
42680         "291"
42681       ],
42682       [
42683         "Estonia (Eesti)",
42684         "ee",
42685         "372"
42686       ],
42687       [
42688         "Ethiopia",
42689         "et",
42690         "251"
42691       ],
42692       [
42693         "Falkland Islands (Islas Malvinas)",
42694         "fk",
42695         "500"
42696       ],
42697       [
42698         "Faroe Islands (Føroyar)",
42699         "fo",
42700         "298"
42701       ],
42702       [
42703         "Fiji",
42704         "fj",
42705         "679"
42706       ],
42707       [
42708         "Finland (Suomi)",
42709         "fi",
42710         "358",
42711         0
42712       ],
42713       [
42714         "France",
42715         "fr",
42716         "33"
42717       ],
42718       [
42719         "French Guiana (Guyane française)",
42720         "gf",
42721         "594"
42722       ],
42723       [
42724         "French Polynesia (Polynésie française)",
42725         "pf",
42726         "689"
42727       ],
42728       [
42729         "Gabon",
42730         "ga",
42731         "241"
42732       ],
42733       [
42734         "Gambia",
42735         "gm",
42736         "220"
42737       ],
42738       [
42739         "Georgia (საქართველო)",
42740         "ge",
42741         "995"
42742       ],
42743       [
42744         "Germany (Deutschland)",
42745         "de",
42746         "49"
42747       ],
42748       [
42749         "Ghana (Gaana)",
42750         "gh",
42751         "233"
42752       ],
42753       [
42754         "Gibraltar",
42755         "gi",
42756         "350"
42757       ],
42758       [
42759         "Greece (Ελλάδα)",
42760         "gr",
42761         "30"
42762       ],
42763       [
42764         "Greenland (Kalaallit Nunaat)",
42765         "gl",
42766         "299"
42767       ],
42768       [
42769         "Grenada",
42770         "gd",
42771         "1473"
42772       ],
42773       [
42774         "Guadeloupe",
42775         "gp",
42776         "590",
42777         0
42778       ],
42779       [
42780         "Guam",
42781         "gu",
42782         "1671"
42783       ],
42784       [
42785         "Guatemala",
42786         "gt",
42787         "502"
42788       ],
42789       [
42790         "Guernsey",
42791         "gg",
42792         "44",
42793         1
42794       ],
42795       [
42796         "Guinea (Guinée)",
42797         "gn",
42798         "224"
42799       ],
42800       [
42801         "Guinea-Bissau (Guiné Bissau)",
42802         "gw",
42803         "245"
42804       ],
42805       [
42806         "Guyana",
42807         "gy",
42808         "592"
42809       ],
42810       [
42811         "Haiti",
42812         "ht",
42813         "509"
42814       ],
42815       [
42816         "Honduras",
42817         "hn",
42818         "504"
42819       ],
42820       [
42821         "Hong Kong (香港)",
42822         "hk",
42823         "852"
42824       ],
42825       [
42826         "Hungary (Magyarország)",
42827         "hu",
42828         "36"
42829       ],
42830       [
42831         "Iceland (Ísland)",
42832         "is",
42833         "354"
42834       ],
42835       [
42836         "India (भारत)",
42837         "in",
42838         "91"
42839       ],
42840       [
42841         "Indonesia",
42842         "id",
42843         "62"
42844       ],
42845       [
42846         "Iran (‫ایران‬‎)",
42847         "ir",
42848         "98"
42849       ],
42850       [
42851         "Iraq (‫العراق‬‎)",
42852         "iq",
42853         "964"
42854       ],
42855       [
42856         "Ireland",
42857         "ie",
42858         "353"
42859       ],
42860       [
42861         "Isle of Man",
42862         "im",
42863         "44",
42864         2
42865       ],
42866       [
42867         "Israel (‫ישראל‬‎)",
42868         "il",
42869         "972"
42870       ],
42871       [
42872         "Italy (Italia)",
42873         "it",
42874         "39",
42875         0
42876       ],
42877       [
42878         "Jamaica",
42879         "jm",
42880         "1876"
42881       ],
42882       [
42883         "Japan (日本)",
42884         "jp",
42885         "81"
42886       ],
42887       [
42888         "Jersey",
42889         "je",
42890         "44",
42891         3
42892       ],
42893       [
42894         "Jordan (‫الأردن‬‎)",
42895         "jo",
42896         "962"
42897       ],
42898       [
42899         "Kazakhstan (Казахстан)",
42900         "kz",
42901         "7",
42902         1
42903       ],
42904       [
42905         "Kenya",
42906         "ke",
42907         "254"
42908       ],
42909       [
42910         "Kiribati",
42911         "ki",
42912         "686"
42913       ],
42914       [
42915         "Kosovo",
42916         "xk",
42917         "383"
42918       ],
42919       [
42920         "Kuwait (‫الكويت‬‎)",
42921         "kw",
42922         "965"
42923       ],
42924       [
42925         "Kyrgyzstan (Кыргызстан)",
42926         "kg",
42927         "996"
42928       ],
42929       [
42930         "Laos (ລາວ)",
42931         "la",
42932         "856"
42933       ],
42934       [
42935         "Latvia (Latvija)",
42936         "lv",
42937         "371"
42938       ],
42939       [
42940         "Lebanon (‫لبنان‬‎)",
42941         "lb",
42942         "961"
42943       ],
42944       [
42945         "Lesotho",
42946         "ls",
42947         "266"
42948       ],
42949       [
42950         "Liberia",
42951         "lr",
42952         "231"
42953       ],
42954       [
42955         "Libya (‫ليبيا‬‎)",
42956         "ly",
42957         "218"
42958       ],
42959       [
42960         "Liechtenstein",
42961         "li",
42962         "423"
42963       ],
42964       [
42965         "Lithuania (Lietuva)",
42966         "lt",
42967         "370"
42968       ],
42969       [
42970         "Luxembourg",
42971         "lu",
42972         "352"
42973       ],
42974       [
42975         "Macau (澳門)",
42976         "mo",
42977         "853"
42978       ],
42979       [
42980         "Macedonia (FYROM) (Македонија)",
42981         "mk",
42982         "389"
42983       ],
42984       [
42985         "Madagascar (Madagasikara)",
42986         "mg",
42987         "261"
42988       ],
42989       [
42990         "Malawi",
42991         "mw",
42992         "265"
42993       ],
42994       [
42995         "Malaysia",
42996         "my",
42997         "60"
42998       ],
42999       [
43000         "Maldives",
43001         "mv",
43002         "960"
43003       ],
43004       [
43005         "Mali",
43006         "ml",
43007         "223"
43008       ],
43009       [
43010         "Malta",
43011         "mt",
43012         "356"
43013       ],
43014       [
43015         "Marshall Islands",
43016         "mh",
43017         "692"
43018       ],
43019       [
43020         "Martinique",
43021         "mq",
43022         "596"
43023       ],
43024       [
43025         "Mauritania (‫موريتانيا‬‎)",
43026         "mr",
43027         "222"
43028       ],
43029       [
43030         "Mauritius (Moris)",
43031         "mu",
43032         "230"
43033       ],
43034       [
43035         "Mayotte",
43036         "yt",
43037         "262",
43038         1
43039       ],
43040       [
43041         "Mexico (México)",
43042         "mx",
43043         "52"
43044       ],
43045       [
43046         "Micronesia",
43047         "fm",
43048         "691"
43049       ],
43050       [
43051         "Moldova (Republica Moldova)",
43052         "md",
43053         "373"
43054       ],
43055       [
43056         "Monaco",
43057         "mc",
43058         "377"
43059       ],
43060       [
43061         "Mongolia (Монгол)",
43062         "mn",
43063         "976"
43064       ],
43065       [
43066         "Montenegro (Crna Gora)",
43067         "me",
43068         "382"
43069       ],
43070       [
43071         "Montserrat",
43072         "ms",
43073         "1664"
43074       ],
43075       [
43076         "Morocco (‫المغرب‬‎)",
43077         "ma",
43078         "212",
43079         0
43080       ],
43081       [
43082         "Mozambique (Moçambique)",
43083         "mz",
43084         "258"
43085       ],
43086       [
43087         "Myanmar (Burma) (မြန်မာ)",
43088         "mm",
43089         "95"
43090       ],
43091       [
43092         "Namibia (Namibië)",
43093         "na",
43094         "264"
43095       ],
43096       [
43097         "Nauru",
43098         "nr",
43099         "674"
43100       ],
43101       [
43102         "Nepal (नेपाल)",
43103         "np",
43104         "977"
43105       ],
43106       [
43107         "Netherlands (Nederland)",
43108         "nl",
43109         "31"
43110       ],
43111       [
43112         "New Caledonia (Nouvelle-Calédonie)",
43113         "nc",
43114         "687"
43115       ],
43116       [
43117         "New Zealand",
43118         "nz",
43119         "64"
43120       ],
43121       [
43122         "Nicaragua",
43123         "ni",
43124         "505"
43125       ],
43126       [
43127         "Niger (Nijar)",
43128         "ne",
43129         "227"
43130       ],
43131       [
43132         "Nigeria",
43133         "ng",
43134         "234"
43135       ],
43136       [
43137         "Niue",
43138         "nu",
43139         "683"
43140       ],
43141       [
43142         "Norfolk Island",
43143         "nf",
43144         "672"
43145       ],
43146       [
43147         "North Korea (조선 민주주의 인민 공화국)",
43148         "kp",
43149         "850"
43150       ],
43151       [
43152         "Northern Mariana Islands",
43153         "mp",
43154         "1670"
43155       ],
43156       [
43157         "Norway (Norge)",
43158         "no",
43159         "47",
43160         0
43161       ],
43162       [
43163         "Oman (‫عُمان‬‎)",
43164         "om",
43165         "968"
43166       ],
43167       [
43168         "Pakistan (‫پاکستان‬‎)",
43169         "pk",
43170         "92"
43171       ],
43172       [
43173         "Palau",
43174         "pw",
43175         "680"
43176       ],
43177       [
43178         "Palestine (‫فلسطين‬‎)",
43179         "ps",
43180         "970"
43181       ],
43182       [
43183         "Panama (Panamá)",
43184         "pa",
43185         "507"
43186       ],
43187       [
43188         "Papua New Guinea",
43189         "pg",
43190         "675"
43191       ],
43192       [
43193         "Paraguay",
43194         "py",
43195         "595"
43196       ],
43197       [
43198         "Peru (Perú)",
43199         "pe",
43200         "51"
43201       ],
43202       [
43203         "Philippines",
43204         "ph",
43205         "63"
43206       ],
43207       [
43208         "Poland (Polska)",
43209         "pl",
43210         "48"
43211       ],
43212       [
43213         "Portugal",
43214         "pt",
43215         "351"
43216       ],
43217       [
43218         "Puerto Rico",
43219         "pr",
43220         "1",
43221         3,
43222         ["787", "939"]
43223       ],
43224       [
43225         "Qatar (‫قطر‬‎)",
43226         "qa",
43227         "974"
43228       ],
43229       [
43230         "Réunion (La Réunion)",
43231         "re",
43232         "262",
43233         0
43234       ],
43235       [
43236         "Romania (România)",
43237         "ro",
43238         "40"
43239       ],
43240       [
43241         "Russia (Россия)",
43242         "ru",
43243         "7",
43244         0
43245       ],
43246       [
43247         "Rwanda",
43248         "rw",
43249         "250"
43250       ],
43251       [
43252         "Saint Barthélemy",
43253         "bl",
43254         "590",
43255         1
43256       ],
43257       [
43258         "Saint Helena",
43259         "sh",
43260         "290"
43261       ],
43262       [
43263         "Saint Kitts and Nevis",
43264         "kn",
43265         "1869"
43266       ],
43267       [
43268         "Saint Lucia",
43269         "lc",
43270         "1758"
43271       ],
43272       [
43273         "Saint Martin (Saint-Martin (partie française))",
43274         "mf",
43275         "590",
43276         2
43277       ],
43278       [
43279         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43280         "pm",
43281         "508"
43282       ],
43283       [
43284         "Saint Vincent and the Grenadines",
43285         "vc",
43286         "1784"
43287       ],
43288       [
43289         "Samoa",
43290         "ws",
43291         "685"
43292       ],
43293       [
43294         "San Marino",
43295         "sm",
43296         "378"
43297       ],
43298       [
43299         "São Tomé and Príncipe (São Tomé e Príncipe)",
43300         "st",
43301         "239"
43302       ],
43303       [
43304         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43305         "sa",
43306         "966"
43307       ],
43308       [
43309         "Senegal (Sénégal)",
43310         "sn",
43311         "221"
43312       ],
43313       [
43314         "Serbia (Србија)",
43315         "rs",
43316         "381"
43317       ],
43318       [
43319         "Seychelles",
43320         "sc",
43321         "248"
43322       ],
43323       [
43324         "Sierra Leone",
43325         "sl",
43326         "232"
43327       ],
43328       [
43329         "Singapore",
43330         "sg",
43331         "65"
43332       ],
43333       [
43334         "Sint Maarten",
43335         "sx",
43336         "1721"
43337       ],
43338       [
43339         "Slovakia (Slovensko)",
43340         "sk",
43341         "421"
43342       ],
43343       [
43344         "Slovenia (Slovenija)",
43345         "si",
43346         "386"
43347       ],
43348       [
43349         "Solomon Islands",
43350         "sb",
43351         "677"
43352       ],
43353       [
43354         "Somalia (Soomaaliya)",
43355         "so",
43356         "252"
43357       ],
43358       [
43359         "South Africa",
43360         "za",
43361         "27"
43362       ],
43363       [
43364         "South Korea (대한민국)",
43365         "kr",
43366         "82"
43367       ],
43368       [
43369         "South Sudan (‫جنوب السودان‬‎)",
43370         "ss",
43371         "211"
43372       ],
43373       [
43374         "Spain (España)",
43375         "es",
43376         "34"
43377       ],
43378       [
43379         "Sri Lanka (ශ්‍රී ලංකාව)",
43380         "lk",
43381         "94"
43382       ],
43383       [
43384         "Sudan (‫السودان‬‎)",
43385         "sd",
43386         "249"
43387       ],
43388       [
43389         "Suriname",
43390         "sr",
43391         "597"
43392       ],
43393       [
43394         "Svalbard and Jan Mayen",
43395         "sj",
43396         "47",
43397         1
43398       ],
43399       [
43400         "Swaziland",
43401         "sz",
43402         "268"
43403       ],
43404       [
43405         "Sweden (Sverige)",
43406         "se",
43407         "46"
43408       ],
43409       [
43410         "Switzerland (Schweiz)",
43411         "ch",
43412         "41"
43413       ],
43414       [
43415         "Syria (‫سوريا‬‎)",
43416         "sy",
43417         "963"
43418       ],
43419       [
43420         "Taiwan (台灣)",
43421         "tw",
43422         "886"
43423       ],
43424       [
43425         "Tajikistan",
43426         "tj",
43427         "992"
43428       ],
43429       [
43430         "Tanzania",
43431         "tz",
43432         "255"
43433       ],
43434       [
43435         "Thailand (ไทย)",
43436         "th",
43437         "66"
43438       ],
43439       [
43440         "Timor-Leste",
43441         "tl",
43442         "670"
43443       ],
43444       [
43445         "Togo",
43446         "tg",
43447         "228"
43448       ],
43449       [
43450         "Tokelau",
43451         "tk",
43452         "690"
43453       ],
43454       [
43455         "Tonga",
43456         "to",
43457         "676"
43458       ],
43459       [
43460         "Trinidad and Tobago",
43461         "tt",
43462         "1868"
43463       ],
43464       [
43465         "Tunisia (‫تونس‬‎)",
43466         "tn",
43467         "216"
43468       ],
43469       [
43470         "Turkey (Türkiye)",
43471         "tr",
43472         "90"
43473       ],
43474       [
43475         "Turkmenistan",
43476         "tm",
43477         "993"
43478       ],
43479       [
43480         "Turks and Caicos Islands",
43481         "tc",
43482         "1649"
43483       ],
43484       [
43485         "Tuvalu",
43486         "tv",
43487         "688"
43488       ],
43489       [
43490         "U.S. Virgin Islands",
43491         "vi",
43492         "1340"
43493       ],
43494       [
43495         "Uganda",
43496         "ug",
43497         "256"
43498       ],
43499       [
43500         "Ukraine (Україна)",
43501         "ua",
43502         "380"
43503       ],
43504       [
43505         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43506         "ae",
43507         "971"
43508       ],
43509       [
43510         "United Kingdom",
43511         "gb",
43512         "44",
43513         0
43514       ],
43515       [
43516         "United States",
43517         "us",
43518         "1",
43519         0
43520       ],
43521       [
43522         "Uruguay",
43523         "uy",
43524         "598"
43525       ],
43526       [
43527         "Uzbekistan (Oʻzbekiston)",
43528         "uz",
43529         "998"
43530       ],
43531       [
43532         "Vanuatu",
43533         "vu",
43534         "678"
43535       ],
43536       [
43537         "Vatican City (Città del Vaticano)",
43538         "va",
43539         "39",
43540         1
43541       ],
43542       [
43543         "Venezuela",
43544         "ve",
43545         "58"
43546       ],
43547       [
43548         "Vietnam (Việt Nam)",
43549         "vn",
43550         "84"
43551       ],
43552       [
43553         "Wallis and Futuna (Wallis-et-Futuna)",
43554         "wf",
43555         "681"
43556       ],
43557       [
43558         "Western Sahara (‫الصحراء الغربية‬‎)",
43559         "eh",
43560         "212",
43561         1
43562       ],
43563       [
43564         "Yemen (‫اليمن‬‎)",
43565         "ye",
43566         "967"
43567       ],
43568       [
43569         "Zambia",
43570         "zm",
43571         "260"
43572       ],
43573       [
43574         "Zimbabwe",
43575         "zw",
43576         "263"
43577       ],
43578       [
43579         "Åland Islands",
43580         "ax",
43581         "358",
43582         1
43583       ]
43584   ];
43585   
43586   return d;
43587 }/**
43588 *    This script refer to:
43589 *    Title: International Telephone Input
43590 *    Author: Jack O'Connor
43591 *    Code version:  v12.1.12
43592 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43593 **/
43594
43595 /**
43596  * @class Roo.bootstrap.PhoneInput
43597  * @extends Roo.bootstrap.TriggerField
43598  * An input with International dial-code selection
43599  
43600  * @cfg {String} defaultDialCode default '+852'
43601  * @cfg {Array} preferedCountries default []
43602   
43603  * @constructor
43604  * Create a new PhoneInput.
43605  * @param {Object} config Configuration options
43606  */
43607
43608 Roo.bootstrap.PhoneInput = function(config) {
43609     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43610 };
43611
43612 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43613         
43614         listWidth: undefined,
43615         
43616         selectedClass: 'active',
43617         
43618         invalidClass : "has-warning",
43619         
43620         validClass: 'has-success',
43621         
43622         allowed: '0123456789',
43623         
43624         max_length: 15,
43625         
43626         /**
43627          * @cfg {String} defaultDialCode The default dial code when initializing the input
43628          */
43629         defaultDialCode: '+852',
43630         
43631         /**
43632          * @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
43633          */
43634         preferedCountries: false,
43635         
43636         getAutoCreate : function()
43637         {
43638             var data = Roo.bootstrap.PhoneInputData();
43639             var align = this.labelAlign || this.parentLabelAlign();
43640             var id = Roo.id();
43641             
43642             this.allCountries = [];
43643             this.dialCodeMapping = [];
43644             
43645             for (var i = 0; i < data.length; i++) {
43646               var c = data[i];
43647               this.allCountries[i] = {
43648                 name: c[0],
43649                 iso2: c[1],
43650                 dialCode: c[2],
43651                 priority: c[3] || 0,
43652                 areaCodes: c[4] || null
43653               };
43654               this.dialCodeMapping[c[2]] = {
43655                   name: c[0],
43656                   iso2: c[1],
43657                   priority: c[3] || 0,
43658                   areaCodes: c[4] || null
43659               };
43660             }
43661             
43662             var cfg = {
43663                 cls: 'form-group',
43664                 cn: []
43665             };
43666             
43667             var input =  {
43668                 tag: 'input',
43669                 id : id,
43670                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43671                 maxlength: this.max_length,
43672                 cls : 'form-control tel-input',
43673                 autocomplete: 'new-password'
43674             };
43675             
43676             var hiddenInput = {
43677                 tag: 'input',
43678                 type: 'hidden',
43679                 cls: 'hidden-tel-input'
43680             };
43681             
43682             if (this.name) {
43683                 hiddenInput.name = this.name;
43684             }
43685             
43686             if (this.disabled) {
43687                 input.disabled = true;
43688             }
43689             
43690             var flag_container = {
43691                 tag: 'div',
43692                 cls: 'flag-box',
43693                 cn: [
43694                     {
43695                         tag: 'div',
43696                         cls: 'flag'
43697                     },
43698                     {
43699                         tag: 'div',
43700                         cls: 'caret'
43701                     }
43702                 ]
43703             };
43704             
43705             var box = {
43706                 tag: 'div',
43707                 cls: this.hasFeedback ? 'has-feedback' : '',
43708                 cn: [
43709                     hiddenInput,
43710                     input,
43711                     {
43712                         tag: 'input',
43713                         cls: 'dial-code-holder',
43714                         disabled: true
43715                     }
43716                 ]
43717             };
43718             
43719             var container = {
43720                 cls: 'roo-select2-container input-group',
43721                 cn: [
43722                     flag_container,
43723                     box
43724                 ]
43725             };
43726             
43727             if (this.fieldLabel.length) {
43728                 var indicator = {
43729                     tag: 'i',
43730                     tooltip: 'This field is required'
43731                 };
43732                 
43733                 var label = {
43734                     tag: 'label',
43735                     'for':  id,
43736                     cls: 'control-label',
43737                     cn: []
43738                 };
43739                 
43740                 var label_text = {
43741                     tag: 'span',
43742                     html: this.fieldLabel
43743                 };
43744                 
43745                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43746                 label.cn = [
43747                     indicator,
43748                     label_text
43749                 ];
43750                 
43751                 if(this.indicatorpos == 'right') {
43752                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43753                     label.cn = [
43754                         label_text,
43755                         indicator
43756                     ];
43757                 }
43758                 
43759                 if(align == 'left') {
43760                     container = {
43761                         tag: 'div',
43762                         cn: [
43763                             container
43764                         ]
43765                     };
43766                     
43767                     if(this.labelWidth > 12){
43768                         label.style = "width: " + this.labelWidth + 'px';
43769                     }
43770                     if(this.labelWidth < 13 && this.labelmd == 0){
43771                         this.labelmd = this.labelWidth;
43772                     }
43773                     if(this.labellg > 0){
43774                         label.cls += ' col-lg-' + this.labellg;
43775                         input.cls += ' col-lg-' + (12 - this.labellg);
43776                     }
43777                     if(this.labelmd > 0){
43778                         label.cls += ' col-md-' + this.labelmd;
43779                         container.cls += ' col-md-' + (12 - this.labelmd);
43780                     }
43781                     if(this.labelsm > 0){
43782                         label.cls += ' col-sm-' + this.labelsm;
43783                         container.cls += ' col-sm-' + (12 - this.labelsm);
43784                     }
43785                     if(this.labelxs > 0){
43786                         label.cls += ' col-xs-' + this.labelxs;
43787                         container.cls += ' col-xs-' + (12 - this.labelxs);
43788                     }
43789                 }
43790             }
43791             
43792             cfg.cn = [
43793                 label,
43794                 container
43795             ];
43796             
43797             var settings = this;
43798             
43799             ['xs','sm','md','lg'].map(function(size){
43800                 if (settings[size]) {
43801                     cfg.cls += ' col-' + size + '-' + settings[size];
43802                 }
43803             });
43804             
43805             this.store = new Roo.data.Store({
43806                 proxy : new Roo.data.MemoryProxy({}),
43807                 reader : new Roo.data.JsonReader({
43808                     fields : [
43809                         {
43810                             'name' : 'name',
43811                             'type' : 'string'
43812                         },
43813                         {
43814                             'name' : 'iso2',
43815                             'type' : 'string'
43816                         },
43817                         {
43818                             'name' : 'dialCode',
43819                             'type' : 'string'
43820                         },
43821                         {
43822                             'name' : 'priority',
43823                             'type' : 'string'
43824                         },
43825                         {
43826                             'name' : 'areaCodes',
43827                             'type' : 'string'
43828                         }
43829                     ]
43830                 })
43831             });
43832             
43833             if(!this.preferedCountries) {
43834                 this.preferedCountries = [
43835                     'hk',
43836                     'gb',
43837                     'us'
43838                 ];
43839             }
43840             
43841             var p = this.preferedCountries.reverse();
43842             
43843             if(p) {
43844                 for (var i = 0; i < p.length; i++) {
43845                     for (var j = 0; j < this.allCountries.length; j++) {
43846                         if(this.allCountries[j].iso2 == p[i]) {
43847                             var t = this.allCountries[j];
43848                             this.allCountries.splice(j,1);
43849                             this.allCountries.unshift(t);
43850                         }
43851                     } 
43852                 }
43853             }
43854             
43855             this.store.proxy.data = {
43856                 success: true,
43857                 data: this.allCountries
43858             };
43859             
43860             return cfg;
43861         },
43862         
43863         initEvents : function()
43864         {
43865             this.createList();
43866             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43867             
43868             this.indicator = this.indicatorEl();
43869             this.flag = this.flagEl();
43870             this.dialCodeHolder = this.dialCodeHolderEl();
43871             
43872             this.trigger = this.el.select('div.flag-box',true).first();
43873             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43874             
43875             var _this = this;
43876             
43877             (function(){
43878                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43879                 _this.list.setWidth(lw);
43880             }).defer(100);
43881             
43882             this.list.on('mouseover', this.onViewOver, this);
43883             this.list.on('mousemove', this.onViewMove, this);
43884             this.inputEl().on("keyup", this.onKeyUp, this);
43885             this.inputEl().on("keypress", this.onKeyPress, this);
43886             
43887             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43888
43889             this.view = new Roo.View(this.list, this.tpl, {
43890                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43891             });
43892             
43893             this.view.on('click', this.onViewClick, this);
43894             this.setValue(this.defaultDialCode);
43895         },
43896         
43897         onTriggerClick : function(e)
43898         {
43899             Roo.log('trigger click');
43900             if(this.disabled){
43901                 return;
43902             }
43903             
43904             if(this.isExpanded()){
43905                 this.collapse();
43906                 this.hasFocus = false;
43907             }else {
43908                 this.store.load({});
43909                 this.hasFocus = true;
43910                 this.expand();
43911             }
43912         },
43913         
43914         isExpanded : function()
43915         {
43916             return this.list.isVisible();
43917         },
43918         
43919         collapse : function()
43920         {
43921             if(!this.isExpanded()){
43922                 return;
43923             }
43924             this.list.hide();
43925             Roo.get(document).un('mousedown', this.collapseIf, this);
43926             Roo.get(document).un('mousewheel', this.collapseIf, this);
43927             this.fireEvent('collapse', this);
43928             this.validate();
43929         },
43930         
43931         expand : function()
43932         {
43933             Roo.log('expand');
43934
43935             if(this.isExpanded() || !this.hasFocus){
43936                 return;
43937             }
43938             
43939             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43940             this.list.setWidth(lw);
43941             
43942             this.list.show();
43943             this.restrictHeight();
43944             
43945             Roo.get(document).on('mousedown', this.collapseIf, this);
43946             Roo.get(document).on('mousewheel', this.collapseIf, this);
43947             
43948             this.fireEvent('expand', this);
43949         },
43950         
43951         restrictHeight : function()
43952         {
43953             this.list.alignTo(this.inputEl(), this.listAlign);
43954             this.list.alignTo(this.inputEl(), this.listAlign);
43955         },
43956         
43957         onViewOver : function(e, t)
43958         {
43959             if(this.inKeyMode){
43960                 return;
43961             }
43962             var item = this.view.findItemFromChild(t);
43963             
43964             if(item){
43965                 var index = this.view.indexOf(item);
43966                 this.select(index, false);
43967             }
43968         },
43969
43970         // private
43971         onViewClick : function(view, doFocus, el, e)
43972         {
43973             var index = this.view.getSelectedIndexes()[0];
43974             
43975             var r = this.store.getAt(index);
43976             
43977             if(r){
43978                 this.onSelect(r, index);
43979             }
43980             if(doFocus !== false && !this.blockFocus){
43981                 this.inputEl().focus();
43982             }
43983         },
43984         
43985         onViewMove : function(e, t)
43986         {
43987             this.inKeyMode = false;
43988         },
43989         
43990         select : function(index, scrollIntoView)
43991         {
43992             this.selectedIndex = index;
43993             this.view.select(index);
43994             if(scrollIntoView !== false){
43995                 var el = this.view.getNode(index);
43996                 if(el){
43997                     this.list.scrollChildIntoView(el, false);
43998                 }
43999             }
44000         },
44001         
44002         createList : function()
44003         {
44004             this.list = Roo.get(document.body).createChild({
44005                 tag: 'ul',
44006                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44007                 style: 'display:none'
44008             });
44009             
44010             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44011         },
44012         
44013         collapseIf : function(e)
44014         {
44015             var in_combo  = e.within(this.el);
44016             var in_list =  e.within(this.list);
44017             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44018             
44019             if (in_combo || in_list || is_list) {
44020                 return;
44021             }
44022             this.collapse();
44023         },
44024         
44025         onSelect : function(record, index)
44026         {
44027             if(this.fireEvent('beforeselect', this, record, index) !== false){
44028                 
44029                 this.setFlagClass(record.data.iso2);
44030                 this.setDialCode(record.data.dialCode);
44031                 this.hasFocus = false;
44032                 this.collapse();
44033                 this.fireEvent('select', this, record, index);
44034             }
44035         },
44036         
44037         flagEl : function()
44038         {
44039             var flag = this.el.select('div.flag',true).first();
44040             if(!flag){
44041                 return false;
44042             }
44043             return flag;
44044         },
44045         
44046         dialCodeHolderEl : function()
44047         {
44048             var d = this.el.select('input.dial-code-holder',true).first();
44049             if(!d){
44050                 return false;
44051             }
44052             return d;
44053         },
44054         
44055         setDialCode : function(v)
44056         {
44057             this.dialCodeHolder.dom.value = '+'+v;
44058         },
44059         
44060         setFlagClass : function(n)
44061         {
44062             this.flag.dom.className = 'flag '+n;
44063         },
44064         
44065         getValue : function()
44066         {
44067             var v = this.inputEl().getValue();
44068             if(this.dialCodeHolder) {
44069                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44070             }
44071             return v;
44072         },
44073         
44074         setValue : function(v)
44075         {
44076             var d = this.getDialCode(v);
44077             
44078             //invalid dial code
44079             if(v.length == 0 || !d || d.length == 0) {
44080                 if(this.rendered){
44081                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44082                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44083                 }
44084                 return;
44085             }
44086             
44087             //valid dial code
44088             this.setFlagClass(this.dialCodeMapping[d].iso2);
44089             this.setDialCode(d);
44090             this.inputEl().dom.value = v.replace('+'+d,'');
44091             this.hiddenEl().dom.value = this.getValue();
44092             
44093             this.validate();
44094         },
44095         
44096         getDialCode : function(v)
44097         {
44098             v = v ||  '';
44099             
44100             if (v.length == 0) {
44101                 return this.dialCodeHolder.dom.value;
44102             }
44103             
44104             var dialCode = "";
44105             if (v.charAt(0) != "+") {
44106                 return false;
44107             }
44108             var numericChars = "";
44109             for (var i = 1; i < v.length; i++) {
44110               var c = v.charAt(i);
44111               if (!isNaN(c)) {
44112                 numericChars += c;
44113                 if (this.dialCodeMapping[numericChars]) {
44114                   dialCode = v.substr(1, i);
44115                 }
44116                 if (numericChars.length == 4) {
44117                   break;
44118                 }
44119               }
44120             }
44121             return dialCode;
44122         },
44123         
44124         reset : function()
44125         {
44126             this.setValue(this.defaultDialCode);
44127             this.validate();
44128         },
44129         
44130         hiddenEl : function()
44131         {
44132             return this.el.select('input.hidden-tel-input',true).first();
44133         },
44134         
44135         // after setting val
44136         onKeyUp : function(e){
44137             this.setValue(this.getValue());
44138         },
44139         
44140         onKeyPress : function(e){
44141             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44142                 e.stopEvent();
44143             }
44144         }
44145         
44146 });
44147 /**
44148  * @class Roo.bootstrap.MoneyField
44149  * @extends Roo.bootstrap.ComboBox
44150  * Bootstrap MoneyField class
44151  * 
44152  * @constructor
44153  * Create a new MoneyField.
44154  * @param {Object} config Configuration options
44155  */
44156
44157 Roo.bootstrap.MoneyField = function(config) {
44158     
44159     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44160     
44161 };
44162
44163 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44164     
44165     /**
44166      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44167      */
44168     allowDecimals : true,
44169     /**
44170      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44171      */
44172     decimalSeparator : ".",
44173     /**
44174      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44175      */
44176     decimalPrecision : 0,
44177     /**
44178      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44179      */
44180     allowNegative : true,
44181     /**
44182      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44183      */
44184     allowZero: true,
44185     /**
44186      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44187      */
44188     minValue : Number.NEGATIVE_INFINITY,
44189     /**
44190      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44191      */
44192     maxValue : Number.MAX_VALUE,
44193     /**
44194      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44195      */
44196     minText : "The minimum value for this field is {0}",
44197     /**
44198      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44199      */
44200     maxText : "The maximum value for this field is {0}",
44201     /**
44202      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44203      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44204      */
44205     nanText : "{0} is not a valid number",
44206     /**
44207      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44208      */
44209     castInt : true,
44210     /**
44211      * @cfg {String} defaults currency of the MoneyField
44212      * value should be in lkey
44213      */
44214     defaultCurrency : false,
44215     /**
44216      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44217      */
44218     thousandsDelimiter : false,
44219     /**
44220      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44221      */
44222     max_length: false,
44223     
44224     inputlg : 9,
44225     inputmd : 9,
44226     inputsm : 9,
44227     inputxs : 6,
44228     
44229     store : false,
44230     
44231     getAutoCreate : function()
44232     {
44233         var align = this.labelAlign || this.parentLabelAlign();
44234         
44235         var id = Roo.id();
44236
44237         var cfg = {
44238             cls: 'form-group',
44239             cn: []
44240         };
44241
44242         var input =  {
44243             tag: 'input',
44244             id : id,
44245             cls : 'form-control roo-money-amount-input',
44246             autocomplete: 'new-password'
44247         };
44248         
44249         var hiddenInput = {
44250             tag: 'input',
44251             type: 'hidden',
44252             id: Roo.id(),
44253             cls: 'hidden-number-input'
44254         };
44255         
44256         if(this.max_length) {
44257             input.maxlength = this.max_length; 
44258         }
44259         
44260         if (this.name) {
44261             hiddenInput.name = this.name;
44262         }
44263
44264         if (this.disabled) {
44265             input.disabled = true;
44266         }
44267
44268         var clg = 12 - this.inputlg;
44269         var cmd = 12 - this.inputmd;
44270         var csm = 12 - this.inputsm;
44271         var cxs = 12 - this.inputxs;
44272         
44273         var container = {
44274             tag : 'div',
44275             cls : 'row roo-money-field',
44276             cn : [
44277                 {
44278                     tag : 'div',
44279                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44280                     cn : [
44281                         {
44282                             tag : 'div',
44283                             cls: 'roo-select2-container input-group',
44284                             cn: [
44285                                 {
44286                                     tag : 'input',
44287                                     cls : 'form-control roo-money-currency-input',
44288                                     autocomplete: 'new-password',
44289                                     readOnly : 1,
44290                                     name : this.currencyName
44291                                 },
44292                                 {
44293                                     tag :'span',
44294                                     cls : 'input-group-addon',
44295                                     cn : [
44296                                         {
44297                                             tag: 'span',
44298                                             cls: 'caret'
44299                                         }
44300                                     ]
44301                                 }
44302                             ]
44303                         }
44304                     ]
44305                 },
44306                 {
44307                     tag : 'div',
44308                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44309                     cn : [
44310                         {
44311                             tag: 'div',
44312                             cls: this.hasFeedback ? 'has-feedback' : '',
44313                             cn: [
44314                                 input
44315                             ]
44316                         }
44317                     ]
44318                 }
44319             ]
44320             
44321         };
44322         
44323         if (this.fieldLabel.length) {
44324             var indicator = {
44325                 tag: 'i',
44326                 tooltip: 'This field is required'
44327             };
44328
44329             var label = {
44330                 tag: 'label',
44331                 'for':  id,
44332                 cls: 'control-label',
44333                 cn: []
44334             };
44335
44336             var label_text = {
44337                 tag: 'span',
44338                 html: this.fieldLabel
44339             };
44340
44341             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44342             label.cn = [
44343                 indicator,
44344                 label_text
44345             ];
44346
44347             if(this.indicatorpos == 'right') {
44348                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44349                 label.cn = [
44350                     label_text,
44351                     indicator
44352                 ];
44353             }
44354
44355             if(align == 'left') {
44356                 container = {
44357                     tag: 'div',
44358                     cn: [
44359                         container
44360                     ]
44361                 };
44362
44363                 if(this.labelWidth > 12){
44364                     label.style = "width: " + this.labelWidth + 'px';
44365                 }
44366                 if(this.labelWidth < 13 && this.labelmd == 0){
44367                     this.labelmd = this.labelWidth;
44368                 }
44369                 if(this.labellg > 0){
44370                     label.cls += ' col-lg-' + this.labellg;
44371                     input.cls += ' col-lg-' + (12 - this.labellg);
44372                 }
44373                 if(this.labelmd > 0){
44374                     label.cls += ' col-md-' + this.labelmd;
44375                     container.cls += ' col-md-' + (12 - this.labelmd);
44376                 }
44377                 if(this.labelsm > 0){
44378                     label.cls += ' col-sm-' + this.labelsm;
44379                     container.cls += ' col-sm-' + (12 - this.labelsm);
44380                 }
44381                 if(this.labelxs > 0){
44382                     label.cls += ' col-xs-' + this.labelxs;
44383                     container.cls += ' col-xs-' + (12 - this.labelxs);
44384                 }
44385             }
44386         }
44387
44388         cfg.cn = [
44389             label,
44390             container,
44391             hiddenInput
44392         ];
44393         
44394         var settings = this;
44395
44396         ['xs','sm','md','lg'].map(function(size){
44397             if (settings[size]) {
44398                 cfg.cls += ' col-' + size + '-' + settings[size];
44399             }
44400         });
44401         
44402         return cfg;
44403     },
44404     
44405     initEvents : function()
44406     {
44407         this.indicator = this.indicatorEl();
44408         
44409         this.initCurrencyEvent();
44410         
44411         this.initNumberEvent();
44412     },
44413     
44414     initCurrencyEvent : function()
44415     {
44416         if (!this.store) {
44417             throw "can not find store for combo";
44418         }
44419         
44420         this.store = Roo.factory(this.store, Roo.data);
44421         this.store.parent = this;
44422         
44423         this.createList();
44424         
44425         this.triggerEl = this.el.select('.input-group-addon', true).first();
44426         
44427         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44428         
44429         var _this = this;
44430         
44431         (function(){
44432             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44433             _this.list.setWidth(lw);
44434         }).defer(100);
44435         
44436         this.list.on('mouseover', this.onViewOver, this);
44437         this.list.on('mousemove', this.onViewMove, this);
44438         this.list.on('scroll', this.onViewScroll, this);
44439         
44440         if(!this.tpl){
44441             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44442         }
44443         
44444         this.view = new Roo.View(this.list, this.tpl, {
44445             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44446         });
44447         
44448         this.view.on('click', this.onViewClick, this);
44449         
44450         this.store.on('beforeload', this.onBeforeLoad, this);
44451         this.store.on('load', this.onLoad, this);
44452         this.store.on('loadexception', this.onLoadException, this);
44453         
44454         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44455             "up" : function(e){
44456                 this.inKeyMode = true;
44457                 this.selectPrev();
44458             },
44459
44460             "down" : function(e){
44461                 if(!this.isExpanded()){
44462                     this.onTriggerClick();
44463                 }else{
44464                     this.inKeyMode = true;
44465                     this.selectNext();
44466                 }
44467             },
44468
44469             "enter" : function(e){
44470                 this.collapse();
44471                 
44472                 if(this.fireEvent("specialkey", this, e)){
44473                     this.onViewClick(false);
44474                 }
44475                 
44476                 return true;
44477             },
44478
44479             "esc" : function(e){
44480                 this.collapse();
44481             },
44482
44483             "tab" : function(e){
44484                 this.collapse();
44485                 
44486                 if(this.fireEvent("specialkey", this, e)){
44487                     this.onViewClick(false);
44488                 }
44489                 
44490                 return true;
44491             },
44492
44493             scope : this,
44494
44495             doRelay : function(foo, bar, hname){
44496                 if(hname == 'down' || this.scope.isExpanded()){
44497                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44498                 }
44499                 return true;
44500             },
44501
44502             forceKeyDown: true
44503         });
44504         
44505         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44506         
44507     },
44508     
44509     initNumberEvent : function(e)
44510     {
44511         this.inputEl().on("keydown" , this.fireKey,  this);
44512         this.inputEl().on("focus", this.onFocus,  this);
44513         this.inputEl().on("blur", this.onBlur,  this);
44514         
44515         this.inputEl().relayEvent('keyup', this);
44516         
44517         if(this.indicator){
44518             this.indicator.addClass('invisible');
44519         }
44520  
44521         this.originalValue = this.getValue();
44522         
44523         if(this.validationEvent == 'keyup'){
44524             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44525             this.inputEl().on('keyup', this.filterValidation, this);
44526         }
44527         else if(this.validationEvent !== false){
44528             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44529         }
44530         
44531         if(this.selectOnFocus){
44532             this.on("focus", this.preFocus, this);
44533             
44534         }
44535         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44536             this.inputEl().on("keypress", this.filterKeys, this);
44537         } else {
44538             this.inputEl().relayEvent('keypress', this);
44539         }
44540         
44541         var allowed = "0123456789";
44542         
44543         if(this.allowDecimals){
44544             allowed += this.decimalSeparator;
44545         }
44546         
44547         if(this.allowNegative){
44548             allowed += "-";
44549         }
44550         
44551         if(this.thousandsDelimiter) {
44552             allowed += ",";
44553         }
44554         
44555         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44556         
44557         var keyPress = function(e){
44558             
44559             var k = e.getKey();
44560             
44561             var c = e.getCharCode();
44562             
44563             if(
44564                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44565                     allowed.indexOf(String.fromCharCode(c)) === -1
44566             ){
44567                 e.stopEvent();
44568                 return;
44569             }
44570             
44571             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44572                 return;
44573             }
44574             
44575             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44576                 e.stopEvent();
44577             }
44578         };
44579         
44580         this.inputEl().on("keypress", keyPress, this);
44581         
44582     },
44583     
44584     onTriggerClick : function(e)
44585     {   
44586         if(this.disabled){
44587             return;
44588         }
44589         
44590         this.page = 0;
44591         this.loadNext = false;
44592         
44593         if(this.isExpanded()){
44594             this.collapse();
44595             return;
44596         }
44597         
44598         this.hasFocus = true;
44599         
44600         if(this.triggerAction == 'all') {
44601             this.doQuery(this.allQuery, true);
44602             return;
44603         }
44604         
44605         this.doQuery(this.getRawValue());
44606     },
44607     
44608     getCurrency : function()
44609     {   
44610         var v = this.currencyEl().getValue();
44611         
44612         return v;
44613     },
44614     
44615     restrictHeight : function()
44616     {
44617         this.list.alignTo(this.currencyEl(), this.listAlign);
44618         this.list.alignTo(this.currencyEl(), this.listAlign);
44619     },
44620     
44621     onViewClick : function(view, doFocus, el, e)
44622     {
44623         var index = this.view.getSelectedIndexes()[0];
44624         
44625         var r = this.store.getAt(index);
44626         
44627         if(r){
44628             this.onSelect(r, index);
44629         }
44630     },
44631     
44632     onSelect : function(record, index){
44633         
44634         if(this.fireEvent('beforeselect', this, record, index) !== false){
44635         
44636             this.setFromCurrencyData(index > -1 ? record.data : false);
44637             
44638             this.collapse();
44639             
44640             this.fireEvent('select', this, record, index);
44641         }
44642     },
44643     
44644     setFromCurrencyData : function(o)
44645     {
44646         var currency = '';
44647         
44648         this.lastCurrency = o;
44649         
44650         if (this.currencyField) {
44651             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44652         } else {
44653             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44654         }
44655         
44656         this.lastSelectionText = currency;
44657         
44658         //setting default currency
44659         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44660             this.setCurrency(this.defaultCurrency);
44661             return;
44662         }
44663         
44664         this.setCurrency(currency);
44665     },
44666     
44667     setFromData : function(o)
44668     {
44669         var c = {};
44670         
44671         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44672         
44673         this.setFromCurrencyData(c);
44674         
44675         var value = '';
44676         
44677         if (this.name) {
44678             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44679         } else {
44680             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44681         }
44682         
44683         this.setValue(value);
44684         
44685     },
44686     
44687     setCurrency : function(v)
44688     {   
44689         this.currencyValue = v;
44690         
44691         if(this.rendered){
44692             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44693             this.validate();
44694         }
44695     },
44696     
44697     setValue : function(v)
44698     {
44699         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44700         
44701         this.value = v;
44702         
44703         if(this.rendered){
44704             
44705             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44706             
44707             this.inputEl().dom.value = (v == '') ? '' :
44708                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44709             
44710             if(!this.allowZero && v === '0') {
44711                 this.hiddenEl().dom.value = '';
44712                 this.inputEl().dom.value = '';
44713             }
44714             
44715             this.validate();
44716         }
44717     },
44718     
44719     getRawValue : function()
44720     {
44721         var v = this.inputEl().getValue();
44722         
44723         return v;
44724     },
44725     
44726     getValue : function()
44727     {
44728         return this.fixPrecision(this.parseValue(this.getRawValue()));
44729     },
44730     
44731     parseValue : function(value)
44732     {
44733         if(this.thousandsDelimiter) {
44734             value += "";
44735             r = new RegExp(",", "g");
44736             value = value.replace(r, "");
44737         }
44738         
44739         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44740         return isNaN(value) ? '' : value;
44741         
44742     },
44743     
44744     fixPrecision : function(value)
44745     {
44746         if(this.thousandsDelimiter) {
44747             value += "";
44748             r = new RegExp(",", "g");
44749             value = value.replace(r, "");
44750         }
44751         
44752         var nan = isNaN(value);
44753         
44754         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44755             return nan ? '' : value;
44756         }
44757         return parseFloat(value).toFixed(this.decimalPrecision);
44758     },
44759     
44760     decimalPrecisionFcn : function(v)
44761     {
44762         return Math.floor(v);
44763     },
44764     
44765     validateValue : function(value)
44766     {
44767         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44768             return false;
44769         }
44770         
44771         var num = this.parseValue(value);
44772         
44773         if(isNaN(num)){
44774             this.markInvalid(String.format(this.nanText, value));
44775             return false;
44776         }
44777         
44778         if(num < this.minValue){
44779             this.markInvalid(String.format(this.minText, this.minValue));
44780             return false;
44781         }
44782         
44783         if(num > this.maxValue){
44784             this.markInvalid(String.format(this.maxText, this.maxValue));
44785             return false;
44786         }
44787         
44788         return true;
44789     },
44790     
44791     validate : function()
44792     {
44793         if(this.disabled || this.allowBlank){
44794             this.markValid();
44795             return true;
44796         }
44797         
44798         var currency = this.getCurrency();
44799         
44800         if(this.validateValue(this.getRawValue()) && currency.length){
44801             this.markValid();
44802             return true;
44803         }
44804         
44805         this.markInvalid();
44806         return false;
44807     },
44808     
44809     getName: function()
44810     {
44811         return this.name;
44812     },
44813     
44814     beforeBlur : function()
44815     {
44816         if(!this.castInt){
44817             return;
44818         }
44819         
44820         var v = this.parseValue(this.getRawValue());
44821         
44822         if(v || v == 0){
44823             this.setValue(v);
44824         }
44825     },
44826     
44827     onBlur : function()
44828     {
44829         this.beforeBlur();
44830         
44831         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44832             //this.el.removeClass(this.focusClass);
44833         }
44834         
44835         this.hasFocus = false;
44836         
44837         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44838             this.validate();
44839         }
44840         
44841         var v = this.getValue();
44842         
44843         if(String(v) !== String(this.startValue)){
44844             this.fireEvent('change', this, v, this.startValue);
44845         }
44846         
44847         this.fireEvent("blur", this);
44848     },
44849     
44850     inputEl : function()
44851     {
44852         return this.el.select('.roo-money-amount-input', true).first();
44853     },
44854     
44855     currencyEl : function()
44856     {
44857         return this.el.select('.roo-money-currency-input', true).first();
44858     },
44859     
44860     hiddenEl : function()
44861     {
44862         return this.el.select('input.hidden-number-input',true).first();
44863     }
44864     
44865 });/**
44866  * @class Roo.bootstrap.BezierSignature
44867  * @extends Roo.bootstrap.Component
44868  * Bootstrap BezierSignature class
44869  * This script refer to:
44870  *    Title: Signature Pad
44871  *    Author: szimek
44872  *    Availability: https://github.com/szimek/signature_pad
44873  *
44874  * @constructor
44875  * Create a new BezierSignature
44876  * @param {Object} config The config object
44877  */
44878
44879 Roo.bootstrap.BezierSignature = function(config){
44880     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44881     this.addEvents({
44882         "resize" : true
44883     });
44884 };
44885
44886 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44887 {
44888      
44889     curve_data: [],
44890     
44891     is_empty: true,
44892     
44893     mouse_btn_down: true,
44894     
44895     /**
44896      * @cfg {int} canvas height
44897      */
44898     canvas_height: '200px',
44899     
44900     /**
44901      * @cfg {float|function} Radius of a single dot.
44902      */ 
44903     dot_size: false,
44904     
44905     /**
44906      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44907      */
44908     min_width: 0.5,
44909     
44910     /**
44911      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44912      */
44913     max_width: 2.5,
44914     
44915     /**
44916      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44917      */
44918     throttle: 16,
44919     
44920     /**
44921      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44922      */
44923     min_distance: 5,
44924     
44925     /**
44926      * @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.
44927      */
44928     bg_color: 'rgba(0, 0, 0, 0)',
44929     
44930     /**
44931      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44932      */
44933     dot_color: 'black',
44934     
44935     /**
44936      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44937      */ 
44938     velocity_filter_weight: 0.7,
44939     
44940     /**
44941      * @cfg {function} Callback when stroke begin. 
44942      */
44943     onBegin: false,
44944     
44945     /**
44946      * @cfg {function} Callback when stroke end.
44947      */
44948     onEnd: false,
44949     
44950     getAutoCreate : function()
44951     {
44952         var cls = 'roo-signature column';
44953         
44954         if(this.cls){
44955             cls += ' ' + this.cls;
44956         }
44957         
44958         var col_sizes = [
44959             'lg',
44960             'md',
44961             'sm',
44962             'xs'
44963         ];
44964         
44965         for(var i = 0; i < col_sizes.length; i++) {
44966             if(this[col_sizes[i]]) {
44967                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44968             }
44969         }
44970         
44971         var cfg = {
44972             tag: 'div',
44973             cls: cls,
44974             cn: [
44975                 {
44976                     tag: 'div',
44977                     cls: 'roo-signature-body',
44978                     cn: [
44979                         {
44980                             tag: 'canvas',
44981                             cls: 'roo-signature-body-canvas',
44982                             height: this.canvas_height,
44983                             width: this.canvas_width
44984                         }
44985                     ]
44986                 },
44987                 {
44988                     tag: 'input',
44989                     type: 'file',
44990                     style: 'display: none'
44991                 }
44992             ]
44993         };
44994         
44995         return cfg;
44996     },
44997     
44998     initEvents: function() 
44999     {
45000         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45001         
45002         var canvas = this.canvasEl();
45003         
45004         // mouse && touch event swapping...
45005         canvas.dom.style.touchAction = 'none';
45006         canvas.dom.style.msTouchAction = 'none';
45007         
45008         this.mouse_btn_down = false;
45009         canvas.on('mousedown', this._handleMouseDown, this);
45010         canvas.on('mousemove', this._handleMouseMove, this);
45011         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45012         
45013         if (window.PointerEvent) {
45014             canvas.on('pointerdown', this._handleMouseDown, this);
45015             canvas.on('pointermove', this._handleMouseMove, this);
45016             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45017         }
45018         
45019         if ('ontouchstart' in window) {
45020             canvas.on('touchstart', this._handleTouchStart, this);
45021             canvas.on('touchmove', this._handleTouchMove, this);
45022             canvas.on('touchend', this._handleTouchEnd, this);
45023         }
45024         
45025         Roo.EventManager.onWindowResize(this.resize, this, true);
45026         
45027         // file input event
45028         this.fileEl().on('change', this.uploadImage, this);
45029         
45030         this.clear();
45031         
45032         this.resize();
45033     },
45034     
45035     resize: function(){
45036         
45037         var canvas = this.canvasEl().dom;
45038         var ctx = this.canvasElCtx();
45039         var img_data = false;
45040         
45041         if(canvas.width > 0) {
45042             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45043         }
45044         // setting canvas width will clean img data
45045         canvas.width = 0;
45046         
45047         var style = window.getComputedStyle ? 
45048             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45049             
45050         var padding_left = parseInt(style.paddingLeft) || 0;
45051         var padding_right = parseInt(style.paddingRight) || 0;
45052         
45053         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45054         
45055         if(img_data) {
45056             ctx.putImageData(img_data, 0, 0);
45057         }
45058     },
45059     
45060     _handleMouseDown: function(e)
45061     {
45062         if (e.browserEvent.which === 1) {
45063             this.mouse_btn_down = true;
45064             this.strokeBegin(e);
45065         }
45066     },
45067     
45068     _handleMouseMove: function (e)
45069     {
45070         if (this.mouse_btn_down) {
45071             this.strokeMoveUpdate(e);
45072         }
45073     },
45074     
45075     _handleMouseUp: function (e)
45076     {
45077         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45078             this.mouse_btn_down = false;
45079             this.strokeEnd(e);
45080         }
45081     },
45082     
45083     _handleTouchStart: function (e) {
45084         
45085         e.preventDefault();
45086         if (e.browserEvent.targetTouches.length === 1) {
45087             // var touch = e.browserEvent.changedTouches[0];
45088             // this.strokeBegin(touch);
45089             
45090              this.strokeBegin(e); // assume e catching the correct xy...
45091         }
45092     },
45093     
45094     _handleTouchMove: function (e) {
45095         e.preventDefault();
45096         // var touch = event.targetTouches[0];
45097         // _this._strokeMoveUpdate(touch);
45098         this.strokeMoveUpdate(e);
45099     },
45100     
45101     _handleTouchEnd: function (e) {
45102         var wasCanvasTouched = e.target === this.canvasEl().dom;
45103         if (wasCanvasTouched) {
45104             e.preventDefault();
45105             // var touch = event.changedTouches[0];
45106             // _this._strokeEnd(touch);
45107             this.strokeEnd(e);
45108         }
45109     },
45110     
45111     reset: function () {
45112         this._lastPoints = [];
45113         this._lastVelocity = 0;
45114         this._lastWidth = (this.min_width + this.max_width) / 2;
45115         this.canvasElCtx().fillStyle = this.dot_color;
45116     },
45117     
45118     strokeMoveUpdate: function(e)
45119     {
45120         this.strokeUpdate(e);
45121         
45122         if (this.throttle) {
45123             this.throttleStroke(this.strokeUpdate, this.throttle);
45124         }
45125         else {
45126             this.strokeUpdate(e);
45127         }
45128     },
45129     
45130     strokeBegin: function(e)
45131     {
45132         var newPointGroup = {
45133             color: this.dot_color,
45134             points: []
45135         };
45136         
45137         if (typeof this.onBegin === 'function') {
45138             this.onBegin(e);
45139         }
45140         
45141         this.curve_data.push(newPointGroup);
45142         this.reset();
45143         this.strokeUpdate(e);
45144     },
45145     
45146     strokeUpdate: function(e)
45147     {
45148         var rect = this.canvasEl().dom.getBoundingClientRect();
45149         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45150         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45151         var lastPoints = lastPointGroup.points;
45152         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45153         var isLastPointTooClose = lastPoint
45154             ? point.distanceTo(lastPoint) <= this.min_distance
45155             : false;
45156         var color = lastPointGroup.color;
45157         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45158             var curve = this.addPoint(point);
45159             if (!lastPoint) {
45160                 this.drawDot({color: color, point: point});
45161             }
45162             else if (curve) {
45163                 this.drawCurve({color: color, curve: curve});
45164             }
45165             lastPoints.push({
45166                 time: point.time,
45167                 x: point.x,
45168                 y: point.y
45169             });
45170         }
45171     },
45172     
45173     strokeEnd: function(e)
45174     {
45175         this.strokeUpdate(e);
45176         if (typeof this.onEnd === 'function') {
45177             this.onEnd(e);
45178         }
45179     },
45180     
45181     addPoint:  function (point) {
45182         var _lastPoints = this._lastPoints;
45183         _lastPoints.push(point);
45184         if (_lastPoints.length > 2) {
45185             if (_lastPoints.length === 3) {
45186                 _lastPoints.unshift(_lastPoints[0]);
45187             }
45188             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45189             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45190             _lastPoints.shift();
45191             return curve;
45192         }
45193         return null;
45194     },
45195     
45196     calculateCurveWidths: function (startPoint, endPoint) {
45197         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45198             (1 - this.velocity_filter_weight) * this._lastVelocity;
45199
45200         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45201         var widths = {
45202             end: newWidth,
45203             start: this._lastWidth
45204         };
45205         
45206         this._lastVelocity = velocity;
45207         this._lastWidth = newWidth;
45208         return widths;
45209     },
45210     
45211     drawDot: function (_a) {
45212         var color = _a.color, point = _a.point;
45213         var ctx = this.canvasElCtx();
45214         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45215         ctx.beginPath();
45216         this.drawCurveSegment(point.x, point.y, width);
45217         ctx.closePath();
45218         ctx.fillStyle = color;
45219         ctx.fill();
45220     },
45221     
45222     drawCurve: function (_a) {
45223         var color = _a.color, curve = _a.curve;
45224         var ctx = this.canvasElCtx();
45225         var widthDelta = curve.endWidth - curve.startWidth;
45226         var drawSteps = Math.floor(curve.length()) * 2;
45227         ctx.beginPath();
45228         ctx.fillStyle = color;
45229         for (var i = 0; i < drawSteps; i += 1) {
45230         var t = i / drawSteps;
45231         var tt = t * t;
45232         var ttt = tt * t;
45233         var u = 1 - t;
45234         var uu = u * u;
45235         var uuu = uu * u;
45236         var x = uuu * curve.startPoint.x;
45237         x += 3 * uu * t * curve.control1.x;
45238         x += 3 * u * tt * curve.control2.x;
45239         x += ttt * curve.endPoint.x;
45240         var y = uuu * curve.startPoint.y;
45241         y += 3 * uu * t * curve.control1.y;
45242         y += 3 * u * tt * curve.control2.y;
45243         y += ttt * curve.endPoint.y;
45244         var width = curve.startWidth + ttt * widthDelta;
45245         this.drawCurveSegment(x, y, width);
45246         }
45247         ctx.closePath();
45248         ctx.fill();
45249     },
45250     
45251     drawCurveSegment: function (x, y, width) {
45252         var ctx = this.canvasElCtx();
45253         ctx.moveTo(x, y);
45254         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45255         this.is_empty = false;
45256     },
45257     
45258     clear: function()
45259     {
45260         var ctx = this.canvasElCtx();
45261         var canvas = this.canvasEl().dom;
45262         ctx.fillStyle = this.bg_color;
45263         ctx.clearRect(0, 0, canvas.width, canvas.height);
45264         ctx.fillRect(0, 0, canvas.width, canvas.height);
45265         this.curve_data = [];
45266         this.reset();
45267         this.is_empty = true;
45268     },
45269     
45270     fileEl: function()
45271     {
45272         return  this.el.select('input',true).first();
45273     },
45274     
45275     canvasEl: function()
45276     {
45277         return this.el.select('canvas',true).first();
45278     },
45279     
45280     canvasElCtx: function()
45281     {
45282         return this.el.select('canvas',true).first().dom.getContext('2d');
45283     },
45284     
45285     getImage: function(type)
45286     {
45287         if(this.is_empty) {
45288             return false;
45289         }
45290         
45291         // encryption ?
45292         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45293     },
45294     
45295     drawFromImage: function(img_src)
45296     {
45297         var img = new Image();
45298         
45299         img.onload = function(){
45300             this.canvasElCtx().drawImage(img, 0, 0);
45301         }.bind(this);
45302         
45303         img.src = img_src;
45304         
45305         this.is_empty = false;
45306     },
45307     
45308     selectImage: function()
45309     {
45310         this.fileEl().dom.click();
45311     },
45312     
45313     uploadImage: function(e)
45314     {
45315         var reader = new FileReader();
45316         
45317         reader.onload = function(e){
45318             var img = new Image();
45319             img.onload = function(){
45320                 this.reset();
45321                 this.canvasElCtx().drawImage(img, 0, 0);
45322             }.bind(this);
45323             img.src = e.target.result;
45324         }.bind(this);
45325         
45326         reader.readAsDataURL(e.target.files[0]);
45327     },
45328     
45329     // Bezier Point Constructor
45330     Point: (function () {
45331         function Point(x, y, time) {
45332             this.x = x;
45333             this.y = y;
45334             this.time = time || Date.now();
45335         }
45336         Point.prototype.distanceTo = function (start) {
45337             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45338         };
45339         Point.prototype.equals = function (other) {
45340             return this.x === other.x && this.y === other.y && this.time === other.time;
45341         };
45342         Point.prototype.velocityFrom = function (start) {
45343             return this.time !== start.time
45344             ? this.distanceTo(start) / (this.time - start.time)
45345             : 0;
45346         };
45347         return Point;
45348     }()),
45349     
45350     
45351     // Bezier Constructor
45352     Bezier: (function () {
45353         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45354             this.startPoint = startPoint;
45355             this.control2 = control2;
45356             this.control1 = control1;
45357             this.endPoint = endPoint;
45358             this.startWidth = startWidth;
45359             this.endWidth = endWidth;
45360         }
45361         Bezier.fromPoints = function (points, widths, scope) {
45362             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45363             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45364             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45365         };
45366         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45367             var dx1 = s1.x - s2.x;
45368             var dy1 = s1.y - s2.y;
45369             var dx2 = s2.x - s3.x;
45370             var dy2 = s2.y - s3.y;
45371             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45372             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45373             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45374             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45375             var dxm = m1.x - m2.x;
45376             var dym = m1.y - m2.y;
45377             var k = l2 / (l1 + l2);
45378             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45379             var tx = s2.x - cm.x;
45380             var ty = s2.y - cm.y;
45381             return {
45382                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45383                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45384             };
45385         };
45386         Bezier.prototype.length = function () {
45387             var steps = 10;
45388             var length = 0;
45389             var px;
45390             var py;
45391             for (var i = 0; i <= steps; i += 1) {
45392                 var t = i / steps;
45393                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45394                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45395                 if (i > 0) {
45396                     var xdiff = cx - px;
45397                     var ydiff = cy - py;
45398                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45399                 }
45400                 px = cx;
45401                 py = cy;
45402             }
45403             return length;
45404         };
45405         Bezier.prototype.point = function (t, start, c1, c2, end) {
45406             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45407             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45408             + (3.0 * c2 * (1.0 - t) * t * t)
45409             + (end * t * t * t);
45410         };
45411         return Bezier;
45412     }()),
45413     
45414     throttleStroke: function(fn, wait) {
45415       if (wait === void 0) { wait = 250; }
45416       var previous = 0;
45417       var timeout = null;
45418       var result;
45419       var storedContext;
45420       var storedArgs;
45421       var later = function () {
45422           previous = Date.now();
45423           timeout = null;
45424           result = fn.apply(storedContext, storedArgs);
45425           if (!timeout) {
45426               storedContext = null;
45427               storedArgs = [];
45428           }
45429       };
45430       return function wrapper() {
45431           var args = [];
45432           for (var _i = 0; _i < arguments.length; _i++) {
45433               args[_i] = arguments[_i];
45434           }
45435           var now = Date.now();
45436           var remaining = wait - (now - previous);
45437           storedContext = this;
45438           storedArgs = args;
45439           if (remaining <= 0 || remaining > wait) {
45440               if (timeout) {
45441                   clearTimeout(timeout);
45442                   timeout = null;
45443               }
45444               previous = now;
45445               result = fn.apply(storedContext, storedArgs);
45446               if (!timeout) {
45447                   storedContext = null;
45448                   storedArgs = [];
45449               }
45450           }
45451           else if (!timeout) {
45452               timeout = window.setTimeout(later, remaining);
45453           }
45454           return result;
45455       };
45456   }
45457   
45458 });
45459
45460  
45461
45462