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             if (this.responsive) {
9131                 width = '';
9132                 left = '';
9133                 hidden = cm.isHidden(i) ? 'display:none' : '';
9134                 splithide = 'display: none';
9135             }
9136             
9137             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9138             if (this.headEl) {
9139                 if (i == last) {
9140                     splithide = 'display:none;';
9141                 }
9142                 
9143                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9144                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9145                 );
9146             }
9147             
9148         }
9149         //Roo.log(styles.join(''));
9150         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9151         
9152     },
9153     
9154     
9155     
9156     onContextMenu : function(e, t)
9157     {
9158         this.processEvent("contextmenu", e);
9159     },
9160     
9161     processEvent : function(name, e)
9162     {
9163         if (name != 'touchstart' ) {
9164             this.fireEvent(name, e);    
9165         }
9166         
9167         var t = e.getTarget();
9168         
9169         var cell = Roo.get(t);
9170         
9171         if(!cell){
9172             return;
9173         }
9174         
9175         if(cell.findParent('tfoot', false, true)){
9176             return;
9177         }
9178         
9179         if(cell.findParent('thead', false, true)){
9180             
9181             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9182                 cell = Roo.get(t).findParent('th', false, true);
9183                 if (!cell) {
9184                     Roo.log("failed to find th in thead?");
9185                     Roo.log(e.getTarget());
9186                     return;
9187                 }
9188             }
9189             
9190             var cellIndex = cell.dom.cellIndex;
9191             
9192             var ename = name == 'touchstart' ? 'click' : name;
9193             this.fireEvent("header" + ename, this, cellIndex, e);
9194             
9195             return;
9196         }
9197         
9198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9199             cell = Roo.get(t).findParent('td', false, true);
9200             if (!cell) {
9201                 Roo.log("failed to find th in tbody?");
9202                 Roo.log(e.getTarget());
9203                 return;
9204             }
9205         }
9206         
9207         var row = cell.findParent('tr', false, true);
9208         var cellIndex = cell.dom.cellIndex;
9209         var rowIndex = row.dom.rowIndex - 1;
9210         
9211         if(row !== false){
9212             
9213             this.fireEvent("row" + name, this, rowIndex, e);
9214             
9215             if(cell !== false){
9216             
9217                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9218             }
9219         }
9220         
9221     },
9222     
9223     onMouseover : function(e, el)
9224     {
9225         var cell = Roo.get(el);
9226         
9227         if(!cell){
9228             return;
9229         }
9230         
9231         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9232             cell = cell.findParent('td', false, true);
9233         }
9234         
9235         var row = cell.findParent('tr', false, true);
9236         var cellIndex = cell.dom.cellIndex;
9237         var rowIndex = row.dom.rowIndex - 1; // start from 0
9238         
9239         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9240         
9241     },
9242     
9243     onMouseout : function(e, el)
9244     {
9245         var cell = Roo.get(el);
9246         
9247         if(!cell){
9248             return;
9249         }
9250         
9251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9252             cell = cell.findParent('td', false, true);
9253         }
9254         
9255         var row = cell.findParent('tr', false, true);
9256         var cellIndex = cell.dom.cellIndex;
9257         var rowIndex = row.dom.rowIndex - 1; // start from 0
9258         
9259         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9260         
9261     },
9262     
9263     onClick : function(e, el)
9264     {
9265         var cell = Roo.get(el);
9266         
9267         if(!cell || (!this.cellSelection && !this.rowSelection)){
9268             return;
9269         }
9270         
9271         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9272             cell = cell.findParent('td', false, true);
9273         }
9274         
9275         if(!cell || typeof(cell) == 'undefined'){
9276             return;
9277         }
9278         
9279         var row = cell.findParent('tr', false, true);
9280         
9281         if(!row || typeof(row) == 'undefined'){
9282             return;
9283         }
9284         
9285         var cellIndex = cell.dom.cellIndex;
9286         var rowIndex = this.getRowIndex(row);
9287         
9288         // why??? - should these not be based on SelectionModel?
9289         //if(this.cellSelection){
9290             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9291         //}
9292         
9293         //if(this.rowSelection){
9294             this.fireEvent('rowclick', this, row, rowIndex, e);
9295         //}
9296          
9297     },
9298         
9299     onDblClick : function(e,el)
9300     {
9301         var cell = Roo.get(el);
9302         
9303         if(!cell || (!this.cellSelection && !this.rowSelection)){
9304             return;
9305         }
9306         
9307         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9308             cell = cell.findParent('td', false, true);
9309         }
9310         
9311         if(!cell || typeof(cell) == 'undefined'){
9312             return;
9313         }
9314         
9315         var row = cell.findParent('tr', false, true);
9316         
9317         if(!row || typeof(row) == 'undefined'){
9318             return;
9319         }
9320         
9321         var cellIndex = cell.dom.cellIndex;
9322         var rowIndex = this.getRowIndex(row);
9323         
9324         if(this.cellSelection){
9325             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9326         }
9327         
9328         if(this.rowSelection){
9329             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9330         }
9331     },
9332     findRowIndex : function(el)
9333     {
9334         var cell = Roo.get(el);
9335         if(!cell) {
9336             return false;
9337         }
9338         var row = cell.findParent('tr', false, true);
9339         
9340         if(!row || typeof(row) == 'undefined'){
9341             return false;
9342         }
9343         return this.getRowIndex(row);
9344     },
9345     sort : function(e,el)
9346     {
9347         var col = Roo.get(el);
9348         
9349         if(!col.hasClass('sortable')){
9350             return;
9351         }
9352         
9353         var sort = col.attr('sort');
9354         var dir = 'ASC';
9355         
9356         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9357             dir = 'DESC';
9358         }
9359         
9360         this.store.sortInfo = {field : sort, direction : dir};
9361         
9362         if (this.footer) {
9363             Roo.log("calling footer first");
9364             this.footer.onClick('first');
9365         } else {
9366         
9367             this.store.load({ params : { start : 0 } });
9368         }
9369     },
9370     
9371     renderHeader : function()
9372     {
9373         var header = {
9374             tag: 'thead',
9375             cn : []
9376         };
9377         
9378         var cm = this.cm;
9379         this.totalWidth = 0;
9380         
9381         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9382             
9383             var config = cm.config[i];
9384             
9385             var c = {
9386                 tag: 'th',
9387                 cls : 'x-hcol-' + i,
9388                 style : '',
9389                 
9390                 html: cm.getColumnHeader(i)
9391             };
9392             
9393             var tooltip = cm.getColumnTooltip(i);
9394             if (tooltip) {
9395                 c.tooltip = tooltip;
9396             }
9397             
9398             
9399             var hh = '';
9400             
9401             if(typeof(config.sortable) != 'undefined' && config.sortable){
9402                 c.cls += ' sortable';
9403                 c.html = '<i class="fa"></i>' + c.html;
9404             }
9405             
9406             // could use BS4 hidden-..-down 
9407             
9408             if(typeof(config.lgHeader) != 'undefined'){
9409                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9410             }
9411             
9412             if(typeof(config.mdHeader) != 'undefined'){
9413                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9414             }
9415             
9416             if(typeof(config.smHeader) != 'undefined'){
9417                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9418             }
9419             
9420             if(typeof(config.xsHeader) != 'undefined'){
9421                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9422             }
9423             
9424             if(hh.length){
9425                 c.html = hh;
9426             }
9427             
9428             if(typeof(config.tooltip) != 'undefined'){
9429                 c.tooltip = config.tooltip;
9430             }
9431             
9432             if(typeof(config.colspan) != 'undefined'){
9433                 c.colspan = config.colspan;
9434             }
9435             
9436             // hidden is handled by CSS now
9437             
9438             if(typeof(config.dataIndex) != 'undefined'){
9439                 c.sort = config.dataIndex;
9440             }
9441             
9442            
9443             
9444             if(typeof(config.align) != 'undefined' && config.align.length){
9445                 c.style += ' text-align:' + config.align + ';';
9446             }
9447             
9448             /* width is done in CSS
9449              *if(typeof(config.width) != 'undefined'){
9450                 c.style += ' width:' + config.width + 'px;';
9451                 this.totalWidth += config.width;
9452             } else {
9453                 this.totalWidth += 100; // assume minimum of 100 per column?
9454             }
9455             */
9456             
9457             if(typeof(config.cls) != 'undefined'){
9458                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9459             }
9460             // this is the bit that doesnt reall work at all...
9461             
9462             if (this.responsive) {
9463                  
9464             
9465                 ['xs','sm','md','lg'].map(function(size){
9466                     
9467                     if(typeof(config[size]) == 'undefined'){
9468                         return;
9469                     }
9470                      
9471                     if (!config[size]) { // 0 = hidden
9472                         // BS 4 '0' is treated as hide that column and below.
9473                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9474                         return;
9475                     }
9476                     
9477                     c.cls += ' col-' + size + '-' + config[size] + (
9478                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9479                     );
9480                     
9481                     
9482                 });
9483             }
9484             // at the end?
9485             
9486             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9487             
9488             
9489             
9490             
9491             header.cn.push(c)
9492         }
9493         
9494         return header;
9495     },
9496     
9497     renderBody : function()
9498     {
9499         var body = {
9500             tag: 'tbody',
9501             cn : [
9502                 {
9503                     tag: 'tr',
9504                     cn : [
9505                         {
9506                             tag : 'td',
9507                             colspan :  this.cm.getColumnCount()
9508                         }
9509                     ]
9510                 }
9511             ]
9512         };
9513         
9514         return body;
9515     },
9516     
9517     renderFooter : function()
9518     {
9519         var footer = {
9520             tag: 'tfoot',
9521             cn : [
9522                 {
9523                     tag: 'tr',
9524                     cn : [
9525                         {
9526                             tag : 'td',
9527                             colspan :  this.cm.getColumnCount()
9528                         }
9529                     ]
9530                 }
9531             ]
9532         };
9533         
9534         return footer;
9535     },
9536     
9537     
9538     
9539     onLoad : function()
9540     {
9541 //        Roo.log('ds onload');
9542         this.clear();
9543         
9544         var _this = this;
9545         var cm = this.cm;
9546         var ds = this.store;
9547         
9548         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9549             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9550             if (_this.store.sortInfo) {
9551                     
9552                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9553                     e.select('i', true).addClass(['fa-arrow-up']);
9554                 }
9555                 
9556                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9557                     e.select('i', true).addClass(['fa-arrow-down']);
9558                 }
9559             }
9560         });
9561         
9562         var tbody =  this.bodyEl;
9563               
9564         if(ds.getCount() > 0){
9565             ds.data.each(function(d,rowIndex){
9566                 var row =  this.renderRow(cm, ds, rowIndex);
9567                 
9568                 tbody.createChild(row);
9569                 
9570                 var _this = this;
9571                 
9572                 if(row.cellObjects.length){
9573                     Roo.each(row.cellObjects, function(r){
9574                         _this.renderCellObject(r);
9575                     })
9576                 }
9577                 
9578             }, this);
9579         }
9580         
9581         var tfoot = this.el.select('tfoot', true).first();
9582         
9583         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9584             
9585             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9586             
9587             var total = this.ds.getTotalCount();
9588             
9589             if(this.footer.pageSize < total){
9590                 this.mainFoot.show();
9591             }
9592         }
9593         
9594         Roo.each(this.el.select('tbody td', true).elements, function(e){
9595             e.on('mouseover', _this.onMouseover, _this);
9596         });
9597         
9598         Roo.each(this.el.select('tbody td', true).elements, function(e){
9599             e.on('mouseout', _this.onMouseout, _this);
9600         });
9601         this.fireEvent('rowsrendered', this);
9602         
9603         this.autoSize();
9604         
9605         this.initCSS(); /// resize cols
9606
9607         
9608     },
9609     
9610     
9611     onUpdate : function(ds,record)
9612     {
9613         this.refreshRow(record);
9614         this.autoSize();
9615     },
9616     
9617     onRemove : function(ds, record, index, isUpdate){
9618         if(isUpdate !== true){
9619             this.fireEvent("beforerowremoved", this, index, record);
9620         }
9621         var bt = this.bodyEl.dom;
9622         
9623         var rows = this.el.select('tbody > tr', true).elements;
9624         
9625         if(typeof(rows[index]) != 'undefined'){
9626             bt.removeChild(rows[index].dom);
9627         }
9628         
9629 //        if(bt.rows[index]){
9630 //            bt.removeChild(bt.rows[index]);
9631 //        }
9632         
9633         if(isUpdate !== true){
9634             //this.stripeRows(index);
9635             //this.syncRowHeights(index, index);
9636             //this.layout();
9637             this.fireEvent("rowremoved", this, index, record);
9638         }
9639     },
9640     
9641     onAdd : function(ds, records, rowIndex)
9642     {
9643         //Roo.log('on Add called');
9644         // - note this does not handle multiple adding very well..
9645         var bt = this.bodyEl.dom;
9646         for (var i =0 ; i < records.length;i++) {
9647             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9648             //Roo.log(records[i]);
9649             //Roo.log(this.store.getAt(rowIndex+i));
9650             this.insertRow(this.store, rowIndex + i, false);
9651             return;
9652         }
9653         
9654     },
9655     
9656     
9657     refreshRow : function(record){
9658         var ds = this.store, index;
9659         if(typeof record == 'number'){
9660             index = record;
9661             record = ds.getAt(index);
9662         }else{
9663             index = ds.indexOf(record);
9664             if (index < 0) {
9665                 return; // should not happen - but seems to 
9666             }
9667         }
9668         this.insertRow(ds, index, true);
9669         this.autoSize();
9670         this.onRemove(ds, record, index+1, true);
9671         this.autoSize();
9672         //this.syncRowHeights(index, index);
9673         //this.layout();
9674         this.fireEvent("rowupdated", this, index, record);
9675     },
9676     // private - called by RowSelection
9677     onRowSelect : function(rowIndex){
9678         var row = this.getRowDom(rowIndex);
9679         row.addClass(['bg-info','info']);
9680     },
9681     // private - called by RowSelection
9682     onRowDeselect : function(rowIndex)
9683     {
9684         if (rowIndex < 0) {
9685             return;
9686         }
9687         var row = this.getRowDom(rowIndex);
9688         row.removeClass(['bg-info','info']);
9689     },
9690       /**
9691      * Focuses the specified row.
9692      * @param {Number} row The row index
9693      */
9694     focusRow : function(row)
9695     {
9696         //Roo.log('GridView.focusRow');
9697         var x = this.bodyEl.dom.scrollLeft;
9698         this.focusCell(row, 0, false);
9699         this.bodyEl.dom.scrollLeft = x;
9700
9701     },
9702      /**
9703      * Focuses the specified cell.
9704      * @param {Number} row The row index
9705      * @param {Number} col The column index
9706      * @param {Boolean} hscroll false to disable horizontal scrolling
9707      */
9708     focusCell : function(row, col, hscroll)
9709     {
9710         //Roo.log('GridView.focusCell');
9711         var el = this.ensureVisible(row, col, hscroll);
9712         // not sure what focusEL achives = it's a <a> pos relative 
9713         //this.focusEl.alignTo(el, "tl-tl");
9714         //if(Roo.isGecko){
9715         //    this.focusEl.focus();
9716         //}else{
9717         //    this.focusEl.focus.defer(1, this.focusEl);
9718         //}
9719     },
9720     
9721      /**
9722      * Scrolls the specified cell into view
9723      * @param {Number} row The row index
9724      * @param {Number} col The column index
9725      * @param {Boolean} hscroll false to disable horizontal scrolling
9726      */
9727     ensureVisible : function(row, col, hscroll)
9728     {
9729         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9730         //return null; //disable for testing.
9731         if(typeof row != "number"){
9732             row = row.rowIndex;
9733         }
9734         if(row < 0 && row >= this.ds.getCount()){
9735             return  null;
9736         }
9737         col = (col !== undefined ? col : 0);
9738         var cm = this.cm;
9739         while(cm.isHidden(col)){
9740             col++;
9741         }
9742
9743         var el = this.getCellDom(row, col);
9744         if(!el){
9745             return null;
9746         }
9747         var c = this.bodyEl.dom;
9748
9749         var ctop = parseInt(el.offsetTop, 10);
9750         var cleft = parseInt(el.offsetLeft, 10);
9751         var cbot = ctop + el.offsetHeight;
9752         var cright = cleft + el.offsetWidth;
9753
9754         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9755         var ch = 0; //?? header is not withing the area?
9756         var stop = parseInt(c.scrollTop, 10);
9757         var sleft = parseInt(c.scrollLeft, 10);
9758         var sbot = stop + ch;
9759         var sright = sleft + c.clientWidth;
9760         /*
9761         Roo.log('GridView.ensureVisible:' +
9762                 ' ctop:' + ctop +
9763                 ' c.clientHeight:' + c.clientHeight +
9764                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9765                 ' stop:' + stop +
9766                 ' cbot:' + cbot +
9767                 ' sbot:' + sbot +
9768                 ' ch:' + ch  
9769                 );
9770         */
9771         if(ctop < stop){
9772             c.scrollTop = ctop;
9773             //Roo.log("set scrolltop to ctop DISABLE?");
9774         }else if(cbot > sbot){
9775             //Roo.log("set scrolltop to cbot-ch");
9776             c.scrollTop = cbot-ch;
9777         }
9778
9779         if(hscroll !== false){
9780             if(cleft < sleft){
9781                 c.scrollLeft = cleft;
9782             }else if(cright > sright){
9783                 c.scrollLeft = cright-c.clientWidth;
9784             }
9785         }
9786
9787         return el;
9788     },
9789     
9790     
9791     insertRow : function(dm, rowIndex, isUpdate){
9792         
9793         if(!isUpdate){
9794             this.fireEvent("beforerowsinserted", this, rowIndex);
9795         }
9796             //var s = this.getScrollState();
9797         var row = this.renderRow(this.cm, this.store, rowIndex);
9798         // insert before rowIndex..
9799         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9800         
9801         var _this = this;
9802                 
9803         if(row.cellObjects.length){
9804             Roo.each(row.cellObjects, function(r){
9805                 _this.renderCellObject(r);
9806             })
9807         }
9808             
9809         if(!isUpdate){
9810             this.fireEvent("rowsinserted", this, rowIndex);
9811             //this.syncRowHeights(firstRow, lastRow);
9812             //this.stripeRows(firstRow);
9813             //this.layout();
9814         }
9815         
9816     },
9817     
9818     
9819     getRowDom : function(rowIndex)
9820     {
9821         var rows = this.el.select('tbody > tr', true).elements;
9822         
9823         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9824         
9825     },
9826     getCellDom : function(rowIndex, colIndex)
9827     {
9828         var row = this.getRowDom(rowIndex);
9829         if (row === false) {
9830             return false;
9831         }
9832         var cols = row.select('td', true).elements;
9833         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9834         
9835     },
9836     
9837     // returns the object tree for a tr..
9838   
9839     
9840     renderRow : function(cm, ds, rowIndex) 
9841     {
9842         var d = ds.getAt(rowIndex);
9843         
9844         var row = {
9845             tag : 'tr',
9846             cls : 'x-row-' + rowIndex,
9847             cn : []
9848         };
9849             
9850         var cellObjects = [];
9851         
9852         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9853             var config = cm.config[i];
9854             
9855             var renderer = cm.getRenderer(i);
9856             var value = '';
9857             var id = false;
9858             
9859             if(typeof(renderer) !== 'undefined'){
9860                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9861             }
9862             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9863             // and are rendered into the cells after the row is rendered - using the id for the element.
9864             
9865             if(typeof(value) === 'object'){
9866                 id = Roo.id();
9867                 cellObjects.push({
9868                     container : id,
9869                     cfg : value 
9870                 })
9871             }
9872             
9873             var rowcfg = {
9874                 record: d,
9875                 rowIndex : rowIndex,
9876                 colIndex : i,
9877                 rowClass : ''
9878             };
9879
9880             this.fireEvent('rowclass', this, rowcfg);
9881             
9882             var td = {
9883                 tag: 'td',
9884                 // this might end up displaying HTML?
9885                 // this is too messy... - better to only do it on columsn you know are going to be too long
9886                 //tooltip : (typeof(value) === 'object') ? '' : value,
9887                 cls : rowcfg.rowClass + ' x-col-' + i,
9888                 style: '',
9889                 html: (typeof(value) === 'object') ? '' : value
9890             };
9891             
9892             if (id) {
9893                 td.id = id;
9894             }
9895             
9896             if(typeof(config.colspan) != 'undefined'){
9897                 td.colspan = config.colspan;
9898             }
9899             
9900             
9901             
9902             if(typeof(config.align) != 'undefined' && config.align.length){
9903                 td.style += ' text-align:' + config.align + ';';
9904             }
9905             if(typeof(config.valign) != 'undefined' && config.valign.length){
9906                 td.style += ' vertical-align:' + config.valign + ';';
9907             }
9908             /*
9909             if(typeof(config.width) != 'undefined'){
9910                 td.style += ' width:' +  config.width + 'px;';
9911             }
9912             */
9913             
9914             if(typeof(config.cursor) != 'undefined'){
9915                 td.style += ' cursor:' +  config.cursor + ';';
9916             }
9917             
9918             if(typeof(config.cls) != 'undefined'){
9919                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9920             }
9921             if (this.responsive) {
9922                 ['xs','sm','md','lg'].map(function(size){
9923                     
9924                     if(typeof(config[size]) == 'undefined'){
9925                         return;
9926                     }
9927                     
9928                     
9929                       
9930                     if (!config[size]) { // 0 = hidden
9931                         // BS 4 '0' is treated as hide that column and below.
9932                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9933                         return;
9934                     }
9935                     
9936                     td.cls += ' col-' + size + '-' + config[size] + (
9937                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9938                     );
9939                      
9940     
9941                 });
9942             }
9943             row.cn.push(td);
9944            
9945         }
9946         
9947         row.cellObjects = cellObjects;
9948         
9949         return row;
9950           
9951     },
9952     
9953     
9954     
9955     onBeforeLoad : function()
9956     {
9957         
9958     },
9959      /**
9960      * Remove all rows
9961      */
9962     clear : function()
9963     {
9964         this.el.select('tbody', true).first().dom.innerHTML = '';
9965     },
9966     /**
9967      * Show or hide a row.
9968      * @param {Number} rowIndex to show or hide
9969      * @param {Boolean} state hide
9970      */
9971     setRowVisibility : function(rowIndex, state)
9972     {
9973         var bt = this.bodyEl.dom;
9974         
9975         var rows = this.el.select('tbody > tr', true).elements;
9976         
9977         if(typeof(rows[rowIndex]) == 'undefined'){
9978             return;
9979         }
9980         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9981         
9982     },
9983     
9984     
9985     getSelectionModel : function(){
9986         if(!this.selModel){
9987             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9988         }
9989         return this.selModel;
9990     },
9991     /*
9992      * Render the Roo.bootstrap object from renderder
9993      */
9994     renderCellObject : function(r)
9995     {
9996         var _this = this;
9997         
9998         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9999         
10000         var t = r.cfg.render(r.container);
10001         
10002         if(r.cfg.cn){
10003             Roo.each(r.cfg.cn, function(c){
10004                 var child = {
10005                     container: t.getChildContainer(),
10006                     cfg: c
10007                 };
10008                 _this.renderCellObject(child);
10009             })
10010         }
10011     },
10012     /**
10013      * get the Row Index from a dom element.
10014      * @param {Roo.Element} row The row to look for
10015      * @returns {Number} the row
10016      */
10017     getRowIndex : function(row)
10018     {
10019         var rowIndex = -1;
10020         
10021         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10022             if(el != row){
10023                 return;
10024             }
10025             
10026             rowIndex = index;
10027         });
10028         
10029         return rowIndex;
10030     },
10031     /**
10032      * get the header TH element for columnIndex
10033      * @param {Number} columnIndex
10034      * @returns {Roo.Element}
10035      */
10036     getHeaderIndex: function(colIndex)
10037     {
10038         var cols = this.headEl.select('th', true).elements;
10039         return cols[colIndex]; 
10040     },
10041     /**
10042      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10043      * @param {domElement} cell to look for
10044      * @returns {Number} the column
10045      */
10046     getCellIndex : function(cell)
10047     {
10048         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10049         if(id){
10050             return parseInt(id[1], 10);
10051         }
10052         return 0;
10053     },
10054      /**
10055      * Returns the grid's underlying element = used by panel.Grid
10056      * @return {Element} The element
10057      */
10058     getGridEl : function(){
10059         return this.el;
10060     },
10061      /**
10062      * Forces a resize - used by panel.Grid
10063      * @return {Element} The element
10064      */
10065     autoSize : function()
10066     {
10067         //var ctr = Roo.get(this.container.dom.parentElement);
10068         var ctr = Roo.get(this.el.dom);
10069         
10070         var thd = this.getGridEl().select('thead',true).first();
10071         var tbd = this.getGridEl().select('tbody', true).first();
10072         var tfd = this.getGridEl().select('tfoot', true).first();
10073         
10074         var cw = ctr.getWidth();
10075         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10076         
10077         if (tbd) {
10078             
10079             tbd.setWidth(ctr.getWidth());
10080             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10081             // this needs fixing for various usage - currently only hydra job advers I think..
10082             //tdb.setHeight(
10083             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10084             //); 
10085             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10086             cw -= barsize;
10087         }
10088         cw = Math.max(cw, this.totalWidth);
10089         this.getGridEl().select('tbody tr',true).setWidth(cw);
10090         this.initCSS();
10091         
10092         // resize 'expandable coloumn?
10093         
10094         return; // we doe not have a view in this design..
10095         
10096     },
10097     onBodyScroll: function()
10098     {
10099         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10100         if(this.headEl){
10101             this.headEl.setStyle({
10102                 'position' : 'relative',
10103                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10104             });
10105         }
10106         
10107         if(this.lazyLoad){
10108             
10109             var scrollHeight = this.bodyEl.dom.scrollHeight;
10110             
10111             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10112             
10113             var height = this.bodyEl.getHeight();
10114             
10115             if(scrollHeight - height == scrollTop) {
10116                 
10117                 var total = this.ds.getTotalCount();
10118                 
10119                 if(this.footer.cursor + this.footer.pageSize < total){
10120                     
10121                     this.footer.ds.load({
10122                         params : {
10123                             start : this.footer.cursor + this.footer.pageSize,
10124                             limit : this.footer.pageSize
10125                         },
10126                         add : true
10127                     });
10128                 }
10129             }
10130             
10131         }
10132     },
10133     onColumnSplitterMoved : function(i, diff)
10134     {
10135         this.userResized = true;
10136         
10137         var cm = this.colModel;
10138         
10139         var w = this.getHeaderIndex(i).getWidth() + diff;
10140         
10141         
10142         cm.setColumnWidth(i, w, true);
10143         this.initCSS();
10144         //var cid = cm.getColumnId(i); << not used in this version?
10145        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10146         
10147         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10148         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10149         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10150 */
10151         //this.updateSplitters();
10152         //this.layout(); << ??
10153         this.fireEvent("columnresize", i, w);
10154     },
10155     onHeaderChange : function()
10156     {
10157         var header = this.renderHeader();
10158         var table = this.el.select('table', true).first();
10159         
10160         this.headEl.remove();
10161         this.headEl = table.createChild(header, this.bodyEl, false);
10162         
10163         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10164             e.on('click', this.sort, this);
10165         }, this);
10166         
10167         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10168             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10169         }
10170         
10171     },
10172     
10173     onHiddenChange : function(colModel, colIndex, hidden)
10174     {
10175         /*
10176         this.cm.setHidden()
10177         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10178         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10179         
10180         this.CSS.updateRule(thSelector, "display", "");
10181         this.CSS.updateRule(tdSelector, "display", "");
10182         
10183         if(hidden){
10184             this.CSS.updateRule(thSelector, "display", "none");
10185             this.CSS.updateRule(tdSelector, "display", "none");
10186         }
10187         */
10188         // onload calls initCSS()
10189         this.onHeaderChange();
10190         this.onLoad();
10191     },
10192     
10193     setColumnWidth: function(col_index, width)
10194     {
10195         // width = "md-2 xs-2..."
10196         if(!this.colModel.config[col_index]) {
10197             return;
10198         }
10199         
10200         var w = width.split(" ");
10201         
10202         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10203         
10204         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10205         
10206         
10207         for(var j = 0; j < w.length; j++) {
10208             
10209             if(!w[j]) {
10210                 continue;
10211             }
10212             
10213             var size_cls = w[j].split("-");
10214             
10215             if(!Number.isInteger(size_cls[1] * 1)) {
10216                 continue;
10217             }
10218             
10219             if(!this.colModel.config[col_index][size_cls[0]]) {
10220                 continue;
10221             }
10222             
10223             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10224                 continue;
10225             }
10226             
10227             h_row[0].classList.replace(
10228                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10229                 "col-"+size_cls[0]+"-"+size_cls[1]
10230             );
10231             
10232             for(var i = 0; i < rows.length; i++) {
10233                 
10234                 var size_cls = w[j].split("-");
10235                 
10236                 if(!Number.isInteger(size_cls[1] * 1)) {
10237                     continue;
10238                 }
10239                 
10240                 if(!this.colModel.config[col_index][size_cls[0]]) {
10241                     continue;
10242                 }
10243                 
10244                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10245                     continue;
10246                 }
10247                 
10248                 rows[i].classList.replace(
10249                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10250                     "col-"+size_cls[0]+"-"+size_cls[1]
10251                 );
10252             }
10253             
10254             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10255         }
10256     }
10257 });
10258
10259 // currently only used to find the split on drag.. 
10260 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10261
10262 /**
10263  * @depricated
10264 */
10265 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10266 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10267 /*
10268  * - LGPL
10269  *
10270  * table cell
10271  * 
10272  */
10273
10274 /**
10275  * @class Roo.bootstrap.TableCell
10276  * @extends Roo.bootstrap.Component
10277  * Bootstrap TableCell class
10278  * @cfg {String} html cell contain text
10279  * @cfg {String} cls cell class
10280  * @cfg {String} tag cell tag (td|th) default td
10281  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10282  * @cfg {String} align Aligns the content in a cell
10283  * @cfg {String} axis Categorizes cells
10284  * @cfg {String} bgcolor Specifies the background color of a cell
10285  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10286  * @cfg {Number} colspan Specifies the number of columns a cell should span
10287  * @cfg {String} headers Specifies one or more header cells a cell is related to
10288  * @cfg {Number} height Sets the height of a cell
10289  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10290  * @cfg {Number} rowspan Sets the number of rows a cell should span
10291  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10292  * @cfg {String} valign Vertical aligns the content in a cell
10293  * @cfg {Number} width Specifies the width of a cell
10294  * 
10295  * @constructor
10296  * Create a new TableCell
10297  * @param {Object} config The config object
10298  */
10299
10300 Roo.bootstrap.TableCell = function(config){
10301     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10302 };
10303
10304 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10305     
10306     html: false,
10307     cls: false,
10308     tag: false,
10309     abbr: false,
10310     align: false,
10311     axis: false,
10312     bgcolor: false,
10313     charoff: false,
10314     colspan: false,
10315     headers: false,
10316     height: false,
10317     nowrap: false,
10318     rowspan: false,
10319     scope: false,
10320     valign: false,
10321     width: false,
10322     
10323     
10324     getAutoCreate : function(){
10325         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10326         
10327         cfg = {
10328             tag: 'td'
10329         };
10330         
10331         if(this.tag){
10332             cfg.tag = this.tag;
10333         }
10334         
10335         if (this.html) {
10336             cfg.html=this.html
10337         }
10338         if (this.cls) {
10339             cfg.cls=this.cls
10340         }
10341         if (this.abbr) {
10342             cfg.abbr=this.abbr
10343         }
10344         if (this.align) {
10345             cfg.align=this.align
10346         }
10347         if (this.axis) {
10348             cfg.axis=this.axis
10349         }
10350         if (this.bgcolor) {
10351             cfg.bgcolor=this.bgcolor
10352         }
10353         if (this.charoff) {
10354             cfg.charoff=this.charoff
10355         }
10356         if (this.colspan) {
10357             cfg.colspan=this.colspan
10358         }
10359         if (this.headers) {
10360             cfg.headers=this.headers
10361         }
10362         if (this.height) {
10363             cfg.height=this.height
10364         }
10365         if (this.nowrap) {
10366             cfg.nowrap=this.nowrap
10367         }
10368         if (this.rowspan) {
10369             cfg.rowspan=this.rowspan
10370         }
10371         if (this.scope) {
10372             cfg.scope=this.scope
10373         }
10374         if (this.valign) {
10375             cfg.valign=this.valign
10376         }
10377         if (this.width) {
10378             cfg.width=this.width
10379         }
10380         
10381         
10382         return cfg;
10383     }
10384    
10385 });
10386
10387  
10388
10389  /*
10390  * - LGPL
10391  *
10392  * table row
10393  * 
10394  */
10395
10396 /**
10397  * @class Roo.bootstrap.TableRow
10398  * @extends Roo.bootstrap.Component
10399  * Bootstrap TableRow class
10400  * @cfg {String} cls row class
10401  * @cfg {String} align Aligns the content in a table row
10402  * @cfg {String} bgcolor Specifies a background color for a table row
10403  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10404  * @cfg {String} valign Vertical aligns the content in a table row
10405  * 
10406  * @constructor
10407  * Create a new TableRow
10408  * @param {Object} config The config object
10409  */
10410
10411 Roo.bootstrap.TableRow = function(config){
10412     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10413 };
10414
10415 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10416     
10417     cls: false,
10418     align: false,
10419     bgcolor: false,
10420     charoff: false,
10421     valign: false,
10422     
10423     getAutoCreate : function(){
10424         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10425         
10426         cfg = {
10427             tag: 'tr'
10428         };
10429             
10430         if(this.cls){
10431             cfg.cls = this.cls;
10432         }
10433         if(this.align){
10434             cfg.align = this.align;
10435         }
10436         if(this.bgcolor){
10437             cfg.bgcolor = this.bgcolor;
10438         }
10439         if(this.charoff){
10440             cfg.charoff = this.charoff;
10441         }
10442         if(this.valign){
10443             cfg.valign = this.valign;
10444         }
10445         
10446         return cfg;
10447     }
10448    
10449 });
10450
10451  
10452
10453  /*
10454  * - LGPL
10455  *
10456  * table body
10457  * 
10458  */
10459
10460 /**
10461  * @class Roo.bootstrap.TableBody
10462  * @extends Roo.bootstrap.Component
10463  * Bootstrap TableBody class
10464  * @cfg {String} cls element class
10465  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10466  * @cfg {String} align Aligns the content inside the element
10467  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10468  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10469  * 
10470  * @constructor
10471  * Create a new TableBody
10472  * @param {Object} config The config object
10473  */
10474
10475 Roo.bootstrap.TableBody = function(config){
10476     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10477 };
10478
10479 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10480     
10481     cls: false,
10482     tag: false,
10483     align: false,
10484     charoff: false,
10485     valign: false,
10486     
10487     getAutoCreate : function(){
10488         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10489         
10490         cfg = {
10491             tag: 'tbody'
10492         };
10493             
10494         if (this.cls) {
10495             cfg.cls=this.cls
10496         }
10497         if(this.tag){
10498             cfg.tag = this.tag;
10499         }
10500         
10501         if(this.align){
10502             cfg.align = this.align;
10503         }
10504         if(this.charoff){
10505             cfg.charoff = this.charoff;
10506         }
10507         if(this.valign){
10508             cfg.valign = this.valign;
10509         }
10510         
10511         return cfg;
10512     }
10513     
10514     
10515 //    initEvents : function()
10516 //    {
10517 //        
10518 //        if(!this.store){
10519 //            return;
10520 //        }
10521 //        
10522 //        this.store = Roo.factory(this.store, Roo.data);
10523 //        this.store.on('load', this.onLoad, this);
10524 //        
10525 //        this.store.load();
10526 //        
10527 //    },
10528 //    
10529 //    onLoad: function () 
10530 //    {   
10531 //        this.fireEvent('load', this);
10532 //    }
10533 //    
10534 //   
10535 });
10536
10537  
10538
10539  /*
10540  * Based on:
10541  * Ext JS Library 1.1.1
10542  * Copyright(c) 2006-2007, Ext JS, LLC.
10543  *
10544  * Originally Released Under LGPL - original licence link has changed is not relivant.
10545  *
10546  * Fork - LGPL
10547  * <script type="text/javascript">
10548  */
10549
10550 // as we use this in bootstrap.
10551 Roo.namespace('Roo.form');
10552  /**
10553  * @class Roo.form.Action
10554  * Internal Class used to handle form actions
10555  * @constructor
10556  * @param {Roo.form.BasicForm} el The form element or its id
10557  * @param {Object} config Configuration options
10558  */
10559
10560  
10561  
10562 // define the action interface
10563 Roo.form.Action = function(form, options){
10564     this.form = form;
10565     this.options = options || {};
10566 };
10567 /**
10568  * Client Validation Failed
10569  * @const 
10570  */
10571 Roo.form.Action.CLIENT_INVALID = 'client';
10572 /**
10573  * Server Validation Failed
10574  * @const 
10575  */
10576 Roo.form.Action.SERVER_INVALID = 'server';
10577  /**
10578  * Connect to Server Failed
10579  * @const 
10580  */
10581 Roo.form.Action.CONNECT_FAILURE = 'connect';
10582 /**
10583  * Reading Data from Server Failed
10584  * @const 
10585  */
10586 Roo.form.Action.LOAD_FAILURE = 'load';
10587
10588 Roo.form.Action.prototype = {
10589     type : 'default',
10590     failureType : undefined,
10591     response : undefined,
10592     result : undefined,
10593
10594     // interface method
10595     run : function(options){
10596
10597     },
10598
10599     // interface method
10600     success : function(response){
10601
10602     },
10603
10604     // interface method
10605     handleResponse : function(response){
10606
10607     },
10608
10609     // default connection failure
10610     failure : function(response){
10611         
10612         this.response = response;
10613         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10614         this.form.afterAction(this, false);
10615     },
10616
10617     processResponse : function(response){
10618         this.response = response;
10619         if(!response.responseText){
10620             return true;
10621         }
10622         this.result = this.handleResponse(response);
10623         return this.result;
10624     },
10625
10626     // utility functions used internally
10627     getUrl : function(appendParams){
10628         var url = this.options.url || this.form.url || this.form.el.dom.action;
10629         if(appendParams){
10630             var p = this.getParams();
10631             if(p){
10632                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10633             }
10634         }
10635         return url;
10636     },
10637
10638     getMethod : function(){
10639         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10640     },
10641
10642     getParams : function(){
10643         var bp = this.form.baseParams;
10644         var p = this.options.params;
10645         if(p){
10646             if(typeof p == "object"){
10647                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10648             }else if(typeof p == 'string' && bp){
10649                 p += '&' + Roo.urlEncode(bp);
10650             }
10651         }else if(bp){
10652             p = Roo.urlEncode(bp);
10653         }
10654         return p;
10655     },
10656
10657     createCallback : function(){
10658         return {
10659             success: this.success,
10660             failure: this.failure,
10661             scope: this,
10662             timeout: (this.form.timeout*1000),
10663             upload: this.form.fileUpload ? this.success : undefined
10664         };
10665     }
10666 };
10667
10668 Roo.form.Action.Submit = function(form, options){
10669     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10670 };
10671
10672 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10673     type : 'submit',
10674
10675     haveProgress : false,
10676     uploadComplete : false,
10677     
10678     // uploadProgress indicator.
10679     uploadProgress : function()
10680     {
10681         if (!this.form.progressUrl) {
10682             return;
10683         }
10684         
10685         if (!this.haveProgress) {
10686             Roo.MessageBox.progress("Uploading", "Uploading");
10687         }
10688         if (this.uploadComplete) {
10689            Roo.MessageBox.hide();
10690            return;
10691         }
10692         
10693         this.haveProgress = true;
10694    
10695         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10696         
10697         var c = new Roo.data.Connection();
10698         c.request({
10699             url : this.form.progressUrl,
10700             params: {
10701                 id : uid
10702             },
10703             method: 'GET',
10704             success : function(req){
10705                //console.log(data);
10706                 var rdata = false;
10707                 var edata;
10708                 try  {
10709                    rdata = Roo.decode(req.responseText)
10710                 } catch (e) {
10711                     Roo.log("Invalid data from server..");
10712                     Roo.log(edata);
10713                     return;
10714                 }
10715                 if (!rdata || !rdata.success) {
10716                     Roo.log(rdata);
10717                     Roo.MessageBox.alert(Roo.encode(rdata));
10718                     return;
10719                 }
10720                 var data = rdata.data;
10721                 
10722                 if (this.uploadComplete) {
10723                    Roo.MessageBox.hide();
10724                    return;
10725                 }
10726                    
10727                 if (data){
10728                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10729                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10730                     );
10731                 }
10732                 this.uploadProgress.defer(2000,this);
10733             },
10734        
10735             failure: function(data) {
10736                 Roo.log('progress url failed ');
10737                 Roo.log(data);
10738             },
10739             scope : this
10740         });
10741            
10742     },
10743     
10744     
10745     run : function()
10746     {
10747         // run get Values on the form, so it syncs any secondary forms.
10748         this.form.getValues();
10749         
10750         var o = this.options;
10751         var method = this.getMethod();
10752         var isPost = method == 'POST';
10753         if(o.clientValidation === false || this.form.isValid()){
10754             
10755             if (this.form.progressUrl) {
10756                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10757                     (new Date() * 1) + '' + Math.random());
10758                     
10759             } 
10760             
10761             
10762             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10763                 form:this.form.el.dom,
10764                 url:this.getUrl(!isPost),
10765                 method: method,
10766                 params:isPost ? this.getParams() : null,
10767                 isUpload: this.form.fileUpload,
10768                 formData : this.form.formData
10769             }));
10770             
10771             this.uploadProgress();
10772
10773         }else if (o.clientValidation !== false){ // client validation failed
10774             this.failureType = Roo.form.Action.CLIENT_INVALID;
10775             this.form.afterAction(this, false);
10776         }
10777     },
10778
10779     success : function(response)
10780     {
10781         this.uploadComplete= true;
10782         if (this.haveProgress) {
10783             Roo.MessageBox.hide();
10784         }
10785         
10786         
10787         var result = this.processResponse(response);
10788         if(result === true || result.success){
10789             this.form.afterAction(this, true);
10790             return;
10791         }
10792         if(result.errors){
10793             this.form.markInvalid(result.errors);
10794             this.failureType = Roo.form.Action.SERVER_INVALID;
10795         }
10796         this.form.afterAction(this, false);
10797     },
10798     failure : function(response)
10799     {
10800         this.uploadComplete= true;
10801         if (this.haveProgress) {
10802             Roo.MessageBox.hide();
10803         }
10804         
10805         this.response = response;
10806         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10807         this.form.afterAction(this, false);
10808     },
10809     
10810     handleResponse : function(response){
10811         if(this.form.errorReader){
10812             var rs = this.form.errorReader.read(response);
10813             var errors = [];
10814             if(rs.records){
10815                 for(var i = 0, len = rs.records.length; i < len; i++) {
10816                     var r = rs.records[i];
10817                     errors[i] = r.data;
10818                 }
10819             }
10820             if(errors.length < 1){
10821                 errors = null;
10822             }
10823             return {
10824                 success : rs.success,
10825                 errors : errors
10826             };
10827         }
10828         var ret = false;
10829         try {
10830             ret = Roo.decode(response.responseText);
10831         } catch (e) {
10832             ret = {
10833                 success: false,
10834                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10835                 errors : []
10836             };
10837         }
10838         return ret;
10839         
10840     }
10841 });
10842
10843
10844 Roo.form.Action.Load = function(form, options){
10845     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10846     this.reader = this.form.reader;
10847 };
10848
10849 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10850     type : 'load',
10851
10852     run : function(){
10853         
10854         Roo.Ajax.request(Roo.apply(
10855                 this.createCallback(), {
10856                     method:this.getMethod(),
10857                     url:this.getUrl(false),
10858                     params:this.getParams()
10859         }));
10860     },
10861
10862     success : function(response){
10863         
10864         var result = this.processResponse(response);
10865         if(result === true || !result.success || !result.data){
10866             this.failureType = Roo.form.Action.LOAD_FAILURE;
10867             this.form.afterAction(this, false);
10868             return;
10869         }
10870         this.form.clearInvalid();
10871         this.form.setValues(result.data);
10872         this.form.afterAction(this, true);
10873     },
10874
10875     handleResponse : function(response){
10876         if(this.form.reader){
10877             var rs = this.form.reader.read(response);
10878             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10879             return {
10880                 success : rs.success,
10881                 data : data
10882             };
10883         }
10884         return Roo.decode(response.responseText);
10885     }
10886 });
10887
10888 Roo.form.Action.ACTION_TYPES = {
10889     'load' : Roo.form.Action.Load,
10890     'submit' : Roo.form.Action.Submit
10891 };/*
10892  * - LGPL
10893  *
10894  * form
10895  *
10896  */
10897
10898 /**
10899  * @class Roo.bootstrap.Form
10900  * @extends Roo.bootstrap.Component
10901  * Bootstrap Form class
10902  * @cfg {String} method  GET | POST (default POST)
10903  * @cfg {String} labelAlign top | left (default top)
10904  * @cfg {String} align left  | right - for navbars
10905  * @cfg {Boolean} loadMask load mask when submit (default true)
10906
10907  *
10908  * @constructor
10909  * Create a new Form
10910  * @param {Object} config The config object
10911  */
10912
10913
10914 Roo.bootstrap.Form = function(config){
10915     
10916     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10917     
10918     Roo.bootstrap.Form.popover.apply();
10919     
10920     this.addEvents({
10921         /**
10922          * @event clientvalidation
10923          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10924          * @param {Form} this
10925          * @param {Boolean} valid true if the form has passed client-side validation
10926          */
10927         clientvalidation: true,
10928         /**
10929          * @event beforeaction
10930          * Fires before any action is performed. Return false to cancel the action.
10931          * @param {Form} this
10932          * @param {Action} action The action to be performed
10933          */
10934         beforeaction: true,
10935         /**
10936          * @event actionfailed
10937          * Fires when an action fails.
10938          * @param {Form} this
10939          * @param {Action} action The action that failed
10940          */
10941         actionfailed : true,
10942         /**
10943          * @event actioncomplete
10944          * Fires when an action is completed.
10945          * @param {Form} this
10946          * @param {Action} action The action that completed
10947          */
10948         actioncomplete : true
10949     });
10950 };
10951
10952 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10953
10954      /**
10955      * @cfg {String} method
10956      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10957      */
10958     method : 'POST',
10959     /**
10960      * @cfg {String} url
10961      * The URL to use for form actions if one isn't supplied in the action options.
10962      */
10963     /**
10964      * @cfg {Boolean} fileUpload
10965      * Set to true if this form is a file upload.
10966      */
10967
10968     /**
10969      * @cfg {Object} baseParams
10970      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10971      */
10972
10973     /**
10974      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10975      */
10976     timeout: 30,
10977     /**
10978      * @cfg {Sting} align (left|right) for navbar forms
10979      */
10980     align : 'left',
10981
10982     // private
10983     activeAction : null,
10984
10985     /**
10986      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10987      * element by passing it or its id or mask the form itself by passing in true.
10988      * @type Mixed
10989      */
10990     waitMsgTarget : false,
10991
10992     loadMask : true,
10993     
10994     /**
10995      * @cfg {Boolean} errorMask (true|false) default false
10996      */
10997     errorMask : false,
10998     
10999     /**
11000      * @cfg {Number} maskOffset Default 100
11001      */
11002     maskOffset : 100,
11003     
11004     /**
11005      * @cfg {Boolean} maskBody
11006      */
11007     maskBody : false,
11008
11009     getAutoCreate : function(){
11010
11011         var cfg = {
11012             tag: 'form',
11013             method : this.method || 'POST',
11014             id : this.id || Roo.id(),
11015             cls : ''
11016         };
11017         if (this.parent().xtype.match(/^Nav/)) {
11018             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11019
11020         }
11021
11022         if (this.labelAlign == 'left' ) {
11023             cfg.cls += ' form-horizontal';
11024         }
11025
11026
11027         return cfg;
11028     },
11029     initEvents : function()
11030     {
11031         this.el.on('submit', this.onSubmit, this);
11032         // this was added as random key presses on the form where triggering form submit.
11033         this.el.on('keypress', function(e) {
11034             if (e.getCharCode() != 13) {
11035                 return true;
11036             }
11037             // we might need to allow it for textareas.. and some other items.
11038             // check e.getTarget().
11039
11040             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11041                 return true;
11042             }
11043
11044             Roo.log("keypress blocked");
11045
11046             e.preventDefault();
11047             return false;
11048         });
11049         
11050     },
11051     // private
11052     onSubmit : function(e){
11053         e.stopEvent();
11054     },
11055
11056      /**
11057      * Returns true if client-side validation on the form is successful.
11058      * @return Boolean
11059      */
11060     isValid : function(){
11061         var items = this.getItems();
11062         var valid = true;
11063         var target = false;
11064         
11065         items.each(function(f){
11066             
11067             if(f.validate()){
11068                 return;
11069             }
11070             
11071             Roo.log('invalid field: ' + f.name);
11072             
11073             valid = false;
11074
11075             if(!target && f.el.isVisible(true)){
11076                 target = f;
11077             }
11078            
11079         });
11080         
11081         if(this.errorMask && !valid){
11082             Roo.bootstrap.Form.popover.mask(this, target);
11083         }
11084         
11085         return valid;
11086     },
11087     
11088     /**
11089      * Returns true if any fields in this form have changed since their original load.
11090      * @return Boolean
11091      */
11092     isDirty : function(){
11093         var dirty = false;
11094         var items = this.getItems();
11095         items.each(function(f){
11096            if(f.isDirty()){
11097                dirty = true;
11098                return false;
11099            }
11100            return true;
11101         });
11102         return dirty;
11103     },
11104      /**
11105      * Performs a predefined action (submit or load) or custom actions you define on this form.
11106      * @param {String} actionName The name of the action type
11107      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11108      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11109      * accept other config options):
11110      * <pre>
11111 Property          Type             Description
11112 ----------------  ---------------  ----------------------------------------------------------------------------------
11113 url               String           The url for the action (defaults to the form's url)
11114 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11115 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11116 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11117                                    validate the form on the client (defaults to false)
11118      * </pre>
11119      * @return {BasicForm} this
11120      */
11121     doAction : function(action, options){
11122         if(typeof action == 'string'){
11123             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11124         }
11125         if(this.fireEvent('beforeaction', this, action) !== false){
11126             this.beforeAction(action);
11127             action.run.defer(100, action);
11128         }
11129         return this;
11130     },
11131
11132     // private
11133     beforeAction : function(action){
11134         var o = action.options;
11135         
11136         if(this.loadMask){
11137             
11138             if(this.maskBody){
11139                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11140             } else {
11141                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11142             }
11143         }
11144         // not really supported yet.. ??
11145
11146         //if(this.waitMsgTarget === true){
11147         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11148         //}else if(this.waitMsgTarget){
11149         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11150         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151         //}else {
11152         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11153        // }
11154
11155     },
11156
11157     // private
11158     afterAction : function(action, success){
11159         this.activeAction = null;
11160         var o = action.options;
11161
11162         if(this.loadMask){
11163             
11164             if(this.maskBody){
11165                 Roo.get(document.body).unmask();
11166             } else {
11167                 this.el.unmask();
11168             }
11169         }
11170         
11171         //if(this.waitMsgTarget === true){
11172 //            this.el.unmask();
11173         //}else if(this.waitMsgTarget){
11174         //    this.waitMsgTarget.unmask();
11175         //}else{
11176         //    Roo.MessageBox.updateProgress(1);
11177         //    Roo.MessageBox.hide();
11178        // }
11179         //
11180         if(success){
11181             if(o.reset){
11182                 this.reset();
11183             }
11184             Roo.callback(o.success, o.scope, [this, action]);
11185             this.fireEvent('actioncomplete', this, action);
11186
11187         }else{
11188
11189             // failure condition..
11190             // we have a scenario where updates need confirming.
11191             // eg. if a locking scenario exists..
11192             // we look for { errors : { needs_confirm : true }} in the response.
11193             if (
11194                 (typeof(action.result) != 'undefined')  &&
11195                 (typeof(action.result.errors) != 'undefined')  &&
11196                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11197            ){
11198                 var _t = this;
11199                 Roo.log("not supported yet");
11200                  /*
11201
11202                 Roo.MessageBox.confirm(
11203                     "Change requires confirmation",
11204                     action.result.errorMsg,
11205                     function(r) {
11206                         if (r != 'yes') {
11207                             return;
11208                         }
11209                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11210                     }
11211
11212                 );
11213                 */
11214
11215
11216                 return;
11217             }
11218
11219             Roo.callback(o.failure, o.scope, [this, action]);
11220             // show an error message if no failed handler is set..
11221             if (!this.hasListener('actionfailed')) {
11222                 Roo.log("need to add dialog support");
11223                 /*
11224                 Roo.MessageBox.alert("Error",
11225                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11226                         action.result.errorMsg :
11227                         "Saving Failed, please check your entries or try again"
11228                 );
11229                 */
11230             }
11231
11232             this.fireEvent('actionfailed', this, action);
11233         }
11234
11235     },
11236     /**
11237      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11238      * @param {String} id The value to search for
11239      * @return Field
11240      */
11241     findField : function(id){
11242         var items = this.getItems();
11243         var field = items.get(id);
11244         if(!field){
11245              items.each(function(f){
11246                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11247                     field = f;
11248                     return false;
11249                 }
11250                 return true;
11251             });
11252         }
11253         return field || null;
11254     },
11255      /**
11256      * Mark fields in this form invalid in bulk.
11257      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11258      * @return {BasicForm} this
11259      */
11260     markInvalid : function(errors){
11261         if(errors instanceof Array){
11262             for(var i = 0, len = errors.length; i < len; i++){
11263                 var fieldError = errors[i];
11264                 var f = this.findField(fieldError.id);
11265                 if(f){
11266                     f.markInvalid(fieldError.msg);
11267                 }
11268             }
11269         }else{
11270             var field, id;
11271             for(id in errors){
11272                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11273                     field.markInvalid(errors[id]);
11274                 }
11275             }
11276         }
11277         //Roo.each(this.childForms || [], function (f) {
11278         //    f.markInvalid(errors);
11279         //});
11280
11281         return this;
11282     },
11283
11284     /**
11285      * Set values for fields in this form in bulk.
11286      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11287      * @return {BasicForm} this
11288      */
11289     setValues : function(values){
11290         if(values instanceof Array){ // array of objects
11291             for(var i = 0, len = values.length; i < len; i++){
11292                 var v = values[i];
11293                 var f = this.findField(v.id);
11294                 if(f){
11295                     f.setValue(v.value);
11296                     if(this.trackResetOnLoad){
11297                         f.originalValue = f.getValue();
11298                     }
11299                 }
11300             }
11301         }else{ // object hash
11302             var field, id;
11303             for(id in values){
11304                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11305
11306                     if (field.setFromData &&
11307                         field.valueField &&
11308                         field.displayField &&
11309                         // combos' with local stores can
11310                         // be queried via setValue()
11311                         // to set their value..
11312                         (field.store && !field.store.isLocal)
11313                         ) {
11314                         // it's a combo
11315                         var sd = { };
11316                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11317                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11318                         field.setFromData(sd);
11319
11320                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11321                         
11322                         field.setFromData(values);
11323                         
11324                     } else {
11325                         field.setValue(values[id]);
11326                     }
11327
11328
11329                     if(this.trackResetOnLoad){
11330                         field.originalValue = field.getValue();
11331                     }
11332                 }
11333             }
11334         }
11335
11336         //Roo.each(this.childForms || [], function (f) {
11337         //    f.setValues(values);
11338         //});
11339
11340         return this;
11341     },
11342
11343     /**
11344      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11345      * they are returned as an array.
11346      * @param {Boolean} asString
11347      * @return {Object}
11348      */
11349     getValues : function(asString){
11350         //if (this.childForms) {
11351             // copy values from the child forms
11352         //    Roo.each(this.childForms, function (f) {
11353         //        this.setValues(f.getValues());
11354         //    }, this);
11355         //}
11356
11357
11358
11359         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11360         if(asString === true){
11361             return fs;
11362         }
11363         return Roo.urlDecode(fs);
11364     },
11365
11366     /**
11367      * Returns the fields in this form as an object with key/value pairs.
11368      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11369      * @return {Object}
11370      */
11371     getFieldValues : function(with_hidden)
11372     {
11373         var items = this.getItems();
11374         var ret = {};
11375         items.each(function(f){
11376             
11377             if (!f.getName()) {
11378                 return;
11379             }
11380             
11381             var v = f.getValue();
11382             
11383             if (f.inputType =='radio') {
11384                 if (typeof(ret[f.getName()]) == 'undefined') {
11385                     ret[f.getName()] = ''; // empty..
11386                 }
11387
11388                 if (!f.el.dom.checked) {
11389                     return;
11390
11391                 }
11392                 v = f.el.dom.value;
11393
11394             }
11395             
11396             if(f.xtype == 'MoneyField'){
11397                 ret[f.currencyName] = f.getCurrency();
11398             }
11399
11400             // not sure if this supported any more..
11401             if ((typeof(v) == 'object') && f.getRawValue) {
11402                 v = f.getRawValue() ; // dates..
11403             }
11404             // combo boxes where name != hiddenName...
11405             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11406                 ret[f.name] = f.getRawValue();
11407             }
11408             ret[f.getName()] = v;
11409         });
11410
11411         return ret;
11412     },
11413
11414     /**
11415      * Clears all invalid messages in this form.
11416      * @return {BasicForm} this
11417      */
11418     clearInvalid : function(){
11419         var items = this.getItems();
11420
11421         items.each(function(f){
11422            f.clearInvalid();
11423         });
11424
11425         return this;
11426     },
11427
11428     /**
11429      * Resets this form.
11430      * @return {BasicForm} this
11431      */
11432     reset : function(){
11433         var items = this.getItems();
11434         items.each(function(f){
11435             f.reset();
11436         });
11437
11438         Roo.each(this.childForms || [], function (f) {
11439             f.reset();
11440         });
11441
11442
11443         return this;
11444     },
11445     
11446     getItems : function()
11447     {
11448         var r=new Roo.util.MixedCollection(false, function(o){
11449             return o.id || (o.id = Roo.id());
11450         });
11451         var iter = function(el) {
11452             if (el.inputEl) {
11453                 r.add(el);
11454             }
11455             if (!el.items) {
11456                 return;
11457             }
11458             Roo.each(el.items,function(e) {
11459                 iter(e);
11460             });
11461         };
11462
11463         iter(this);
11464         return r;
11465     },
11466     
11467     hideFields : function(items)
11468     {
11469         Roo.each(items, function(i){
11470             
11471             var f = this.findField(i);
11472             
11473             if(!f){
11474                 return;
11475             }
11476             
11477             f.hide();
11478             
11479         }, this);
11480     },
11481     
11482     showFields : function(items)
11483     {
11484         Roo.each(items, function(i){
11485             
11486             var f = this.findField(i);
11487             
11488             if(!f){
11489                 return;
11490             }
11491             
11492             f.show();
11493             
11494         }, this);
11495     }
11496
11497 });
11498
11499 Roo.apply(Roo.bootstrap.Form, {
11500     
11501     popover : {
11502         
11503         padding : 5,
11504         
11505         isApplied : false,
11506         
11507         isMasked : false,
11508         
11509         form : false,
11510         
11511         target : false,
11512         
11513         toolTip : false,
11514         
11515         intervalID : false,
11516         
11517         maskEl : false,
11518         
11519         apply : function()
11520         {
11521             if(this.isApplied){
11522                 return;
11523             }
11524             
11525             this.maskEl = {
11526                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11527                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11528                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11529                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11530             };
11531             
11532             this.maskEl.top.enableDisplayMode("block");
11533             this.maskEl.left.enableDisplayMode("block");
11534             this.maskEl.bottom.enableDisplayMode("block");
11535             this.maskEl.right.enableDisplayMode("block");
11536             
11537             this.toolTip = new Roo.bootstrap.Tooltip({
11538                 cls : 'roo-form-error-popover',
11539                 alignment : {
11540                     'left' : ['r-l', [-2,0], 'right'],
11541                     'right' : ['l-r', [2,0], 'left'],
11542                     'bottom' : ['tl-bl', [0,2], 'top'],
11543                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11544                 }
11545             });
11546             
11547             this.toolTip.render(Roo.get(document.body));
11548
11549             this.toolTip.el.enableDisplayMode("block");
11550             
11551             Roo.get(document.body).on('click', function(){
11552                 this.unmask();
11553             }, this);
11554             
11555             Roo.get(document.body).on('touchstart', function(){
11556                 this.unmask();
11557             }, this);
11558             
11559             this.isApplied = true
11560         },
11561         
11562         mask : function(form, target)
11563         {
11564             this.form = form;
11565             
11566             this.target = target;
11567             
11568             if(!this.form.errorMask || !target.el){
11569                 return;
11570             }
11571             
11572             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11573             
11574             Roo.log(scrollable);
11575             
11576             var ot = this.target.el.calcOffsetsTo(scrollable);
11577             
11578             var scrollTo = ot[1] - this.form.maskOffset;
11579             
11580             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11581             
11582             scrollable.scrollTo('top', scrollTo);
11583             
11584             var box = this.target.el.getBox();
11585             Roo.log(box);
11586             var zIndex = Roo.bootstrap.Modal.zIndex++;
11587
11588             
11589             this.maskEl.top.setStyle('position', 'absolute');
11590             this.maskEl.top.setStyle('z-index', zIndex);
11591             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11592             this.maskEl.top.setLeft(0);
11593             this.maskEl.top.setTop(0);
11594             this.maskEl.top.show();
11595             
11596             this.maskEl.left.setStyle('position', 'absolute');
11597             this.maskEl.left.setStyle('z-index', zIndex);
11598             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11599             this.maskEl.left.setLeft(0);
11600             this.maskEl.left.setTop(box.y - this.padding);
11601             this.maskEl.left.show();
11602
11603             this.maskEl.bottom.setStyle('position', 'absolute');
11604             this.maskEl.bottom.setStyle('z-index', zIndex);
11605             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11606             this.maskEl.bottom.setLeft(0);
11607             this.maskEl.bottom.setTop(box.bottom + this.padding);
11608             this.maskEl.bottom.show();
11609
11610             this.maskEl.right.setStyle('position', 'absolute');
11611             this.maskEl.right.setStyle('z-index', zIndex);
11612             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11613             this.maskEl.right.setLeft(box.right + this.padding);
11614             this.maskEl.right.setTop(box.y - this.padding);
11615             this.maskEl.right.show();
11616
11617             this.toolTip.bindEl = this.target.el;
11618
11619             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11620
11621             var tip = this.target.blankText;
11622
11623             if(this.target.getValue() !== '' ) {
11624                 
11625                 if (this.target.invalidText.length) {
11626                     tip = this.target.invalidText;
11627                 } else if (this.target.regexText.length){
11628                     tip = this.target.regexText;
11629                 }
11630             }
11631
11632             this.toolTip.show(tip);
11633
11634             this.intervalID = window.setInterval(function() {
11635                 Roo.bootstrap.Form.popover.unmask();
11636             }, 10000);
11637
11638             window.onwheel = function(){ return false;};
11639             
11640             (function(){ this.isMasked = true; }).defer(500, this);
11641             
11642         },
11643         
11644         unmask : function()
11645         {
11646             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11647                 return;
11648             }
11649             
11650             this.maskEl.top.setStyle('position', 'absolute');
11651             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11652             this.maskEl.top.hide();
11653
11654             this.maskEl.left.setStyle('position', 'absolute');
11655             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11656             this.maskEl.left.hide();
11657
11658             this.maskEl.bottom.setStyle('position', 'absolute');
11659             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11660             this.maskEl.bottom.hide();
11661
11662             this.maskEl.right.setStyle('position', 'absolute');
11663             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11664             this.maskEl.right.hide();
11665             
11666             this.toolTip.hide();
11667             
11668             this.toolTip.el.hide();
11669             
11670             window.onwheel = function(){ return true;};
11671             
11672             if(this.intervalID){
11673                 window.clearInterval(this.intervalID);
11674                 this.intervalID = false;
11675             }
11676             
11677             this.isMasked = false;
11678             
11679         }
11680         
11681     }
11682     
11683 });
11684
11685 /*
11686  * Based on:
11687  * Ext JS Library 1.1.1
11688  * Copyright(c) 2006-2007, Ext JS, LLC.
11689  *
11690  * Originally Released Under LGPL - original licence link has changed is not relivant.
11691  *
11692  * Fork - LGPL
11693  * <script type="text/javascript">
11694  */
11695 /**
11696  * @class Roo.form.VTypes
11697  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11698  * @singleton
11699  */
11700 Roo.form.VTypes = function(){
11701     // closure these in so they are only created once.
11702     var alpha = /^[a-zA-Z_]+$/;
11703     var alphanum = /^[a-zA-Z0-9_]+$/;
11704     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11705     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11706
11707     // All these messages and functions are configurable
11708     return {
11709         /**
11710          * The function used to validate email addresses
11711          * @param {String} value The email address
11712          */
11713         'email' : function(v){
11714             return email.test(v);
11715         },
11716         /**
11717          * The error text to display when the email validation function returns false
11718          * @type String
11719          */
11720         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11721         /**
11722          * The keystroke filter mask to be applied on email input
11723          * @type RegExp
11724          */
11725         'emailMask' : /[a-z0-9_\.\-@]/i,
11726
11727         /**
11728          * The function used to validate URLs
11729          * @param {String} value The URL
11730          */
11731         'url' : function(v){
11732             return url.test(v);
11733         },
11734         /**
11735          * The error text to display when the url validation function returns false
11736          * @type String
11737          */
11738         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11739         
11740         /**
11741          * The function used to validate alpha values
11742          * @param {String} value The value
11743          */
11744         'alpha' : function(v){
11745             return alpha.test(v);
11746         },
11747         /**
11748          * The error text to display when the alpha validation function returns false
11749          * @type String
11750          */
11751         'alphaText' : 'This field should only contain letters and _',
11752         /**
11753          * The keystroke filter mask to be applied on alpha input
11754          * @type RegExp
11755          */
11756         'alphaMask' : /[a-z_]/i,
11757
11758         /**
11759          * The function used to validate alphanumeric values
11760          * @param {String} value The value
11761          */
11762         'alphanum' : function(v){
11763             return alphanum.test(v);
11764         },
11765         /**
11766          * The error text to display when the alphanumeric validation function returns false
11767          * @type String
11768          */
11769         'alphanumText' : 'This field should only contain letters, numbers and _',
11770         /**
11771          * The keystroke filter mask to be applied on alphanumeric input
11772          * @type RegExp
11773          */
11774         'alphanumMask' : /[a-z0-9_]/i
11775     };
11776 }();/*
11777  * - LGPL
11778  *
11779  * Input
11780  * 
11781  */
11782
11783 /**
11784  * @class Roo.bootstrap.Input
11785  * @extends Roo.bootstrap.Component
11786  * Bootstrap Input class
11787  * @cfg {Boolean} disabled is it disabled
11788  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11789  * @cfg {String} name name of the input
11790  * @cfg {string} fieldLabel - the label associated
11791  * @cfg {string} placeholder - placeholder to put in text.
11792  * @cfg {string}  before - input group add on before
11793  * @cfg {string} after - input group add on after
11794  * @cfg {string} size - (lg|sm) or leave empty..
11795  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11796  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11797  * @cfg {Number} md colspan out of 12 for computer-sized screens
11798  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11799  * @cfg {string} value default value of the input
11800  * @cfg {Number} labelWidth set the width of label 
11801  * @cfg {Number} labellg set the width of label (1-12)
11802  * @cfg {Number} labelmd set the width of label (1-12)
11803  * @cfg {Number} labelsm set the width of label (1-12)
11804  * @cfg {Number} labelxs set the width of label (1-12)
11805  * @cfg {String} labelAlign (top|left)
11806  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11807  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11808  * @cfg {String} indicatorpos (left|right) default left
11809  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11810  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11811  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11812
11813  * @cfg {String} align (left|center|right) Default left
11814  * @cfg {Boolean} forceFeedback (true|false) Default false
11815  * 
11816  * @constructor
11817  * Create a new Input
11818  * @param {Object} config The config object
11819  */
11820
11821 Roo.bootstrap.Input = function(config){
11822     
11823     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11824     
11825     this.addEvents({
11826         /**
11827          * @event focus
11828          * Fires when this field receives input focus.
11829          * @param {Roo.form.Field} this
11830          */
11831         focus : true,
11832         /**
11833          * @event blur
11834          * Fires when this field loses input focus.
11835          * @param {Roo.form.Field} this
11836          */
11837         blur : true,
11838         /**
11839          * @event specialkey
11840          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11841          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11842          * @param {Roo.form.Field} this
11843          * @param {Roo.EventObject} e The event object
11844          */
11845         specialkey : true,
11846         /**
11847          * @event change
11848          * Fires just before the field blurs if the field value has changed.
11849          * @param {Roo.form.Field} this
11850          * @param {Mixed} newValue The new value
11851          * @param {Mixed} oldValue The original value
11852          */
11853         change : true,
11854         /**
11855          * @event invalid
11856          * Fires after the field has been marked as invalid.
11857          * @param {Roo.form.Field} this
11858          * @param {String} msg The validation message
11859          */
11860         invalid : true,
11861         /**
11862          * @event valid
11863          * Fires after the field has been validated with no errors.
11864          * @param {Roo.form.Field} this
11865          */
11866         valid : true,
11867          /**
11868          * @event keyup
11869          * Fires after the key up
11870          * @param {Roo.form.Field} this
11871          * @param {Roo.EventObject}  e The event Object
11872          */
11873         keyup : true,
11874         /**
11875          * @event paste
11876          * Fires after the user pastes into input
11877          * @param {Roo.form.Field} this
11878          * @param {Roo.EventObject}  e The event Object
11879          */
11880         paste : true
11881     });
11882 };
11883
11884 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11885      /**
11886      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11887       automatic validation (defaults to "keyup").
11888      */
11889     validationEvent : "keyup",
11890      /**
11891      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11892      */
11893     validateOnBlur : true,
11894     /**
11895      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11896      */
11897     validationDelay : 250,
11898      /**
11899      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11900      */
11901     focusClass : "x-form-focus",  // not needed???
11902     
11903        
11904     /**
11905      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11906      */
11907     invalidClass : "has-warning",
11908     
11909     /**
11910      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11911      */
11912     validClass : "has-success",
11913     
11914     /**
11915      * @cfg {Boolean} hasFeedback (true|false) default true
11916      */
11917     hasFeedback : true,
11918     
11919     /**
11920      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11921      */
11922     invalidFeedbackClass : "glyphicon-warning-sign",
11923     
11924     /**
11925      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11926      */
11927     validFeedbackClass : "glyphicon-ok",
11928     
11929     /**
11930      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11931      */
11932     selectOnFocus : false,
11933     
11934      /**
11935      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11936      */
11937     maskRe : null,
11938        /**
11939      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11940      */
11941     vtype : null,
11942     
11943       /**
11944      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11945      */
11946     disableKeyFilter : false,
11947     
11948        /**
11949      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11950      */
11951     disabled : false,
11952      /**
11953      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11954      */
11955     allowBlank : true,
11956     /**
11957      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11958      */
11959     blankText : "Please complete this mandatory field",
11960     
11961      /**
11962      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11963      */
11964     minLength : 0,
11965     /**
11966      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11967      */
11968     maxLength : Number.MAX_VALUE,
11969     /**
11970      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11971      */
11972     minLengthText : "The minimum length for this field is {0}",
11973     /**
11974      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11975      */
11976     maxLengthText : "The maximum length for this field is {0}",
11977   
11978     
11979     /**
11980      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11981      * If available, this function will be called only after the basic validators all return true, and will be passed the
11982      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11983      */
11984     validator : null,
11985     /**
11986      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11987      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11988      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11989      */
11990     regex : null,
11991     /**
11992      * @cfg {String} regexText -- Depricated - use Invalid Text
11993      */
11994     regexText : "",
11995     
11996     /**
11997      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11998      */
11999     invalidText : "",
12000     
12001     
12002     
12003     autocomplete: false,
12004     
12005     
12006     fieldLabel : '',
12007     inputType : 'text',
12008     
12009     name : false,
12010     placeholder: false,
12011     before : false,
12012     after : false,
12013     size : false,
12014     hasFocus : false,
12015     preventMark: false,
12016     isFormField : true,
12017     value : '',
12018     labelWidth : 2,
12019     labelAlign : false,
12020     readOnly : false,
12021     align : false,
12022     formatedValue : false,
12023     forceFeedback : false,
12024     
12025     indicatorpos : 'left',
12026     
12027     labellg : 0,
12028     labelmd : 0,
12029     labelsm : 0,
12030     labelxs : 0,
12031     
12032     capture : '',
12033     accept : '',
12034     
12035     parentLabelAlign : function()
12036     {
12037         var parent = this;
12038         while (parent.parent()) {
12039             parent = parent.parent();
12040             if (typeof(parent.labelAlign) !='undefined') {
12041                 return parent.labelAlign;
12042             }
12043         }
12044         return 'left';
12045         
12046     },
12047     
12048     getAutoCreate : function()
12049     {
12050         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12051         
12052         var id = Roo.id();
12053         
12054         var cfg = {};
12055         
12056         if(this.inputType != 'hidden'){
12057             cfg.cls = 'form-group' //input-group
12058         }
12059         
12060         var input =  {
12061             tag: 'input',
12062             id : id,
12063             type : this.inputType,
12064             value : this.value,
12065             cls : 'form-control',
12066             placeholder : this.placeholder || '',
12067             autocomplete : this.autocomplete || 'new-password'
12068         };
12069         if (this.inputType == 'file') {
12070             input.style = 'overflow:hidden'; // why not in CSS?
12071         }
12072         
12073         if(this.capture.length){
12074             input.capture = this.capture;
12075         }
12076         
12077         if(this.accept.length){
12078             input.accept = this.accept + "/*";
12079         }
12080         
12081         if(this.align){
12082             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12083         }
12084         
12085         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12086             input.maxLength = this.maxLength;
12087         }
12088         
12089         if (this.disabled) {
12090             input.disabled=true;
12091         }
12092         
12093         if (this.readOnly) {
12094             input.readonly=true;
12095         }
12096         
12097         if (this.name) {
12098             input.name = this.name;
12099         }
12100         
12101         if (this.size) {
12102             input.cls += ' input-' + this.size;
12103         }
12104         
12105         var settings=this;
12106         ['xs','sm','md','lg'].map(function(size){
12107             if (settings[size]) {
12108                 cfg.cls += ' col-' + size + '-' + settings[size];
12109             }
12110         });
12111         
12112         var inputblock = input;
12113         
12114         var feedback = {
12115             tag: 'span',
12116             cls: 'glyphicon form-control-feedback'
12117         };
12118             
12119         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12120             
12121             inputblock = {
12122                 cls : 'has-feedback',
12123                 cn :  [
12124                     input,
12125                     feedback
12126                 ] 
12127             };  
12128         }
12129         
12130         if (this.before || this.after) {
12131             
12132             inputblock = {
12133                 cls : 'input-group',
12134                 cn :  [] 
12135             };
12136             
12137             if (this.before && typeof(this.before) == 'string') {
12138                 
12139                 inputblock.cn.push({
12140                     tag :'span',
12141                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12142                     html : this.before
12143                 });
12144             }
12145             if (this.before && typeof(this.before) == 'object') {
12146                 this.before = Roo.factory(this.before);
12147                 
12148                 inputblock.cn.push({
12149                     tag :'span',
12150                     cls : 'roo-input-before input-group-prepend   input-group-' +
12151                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12152                 });
12153             }
12154             
12155             inputblock.cn.push(input);
12156             
12157             if (this.after && typeof(this.after) == 'string') {
12158                 inputblock.cn.push({
12159                     tag :'span',
12160                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12161                     html : this.after
12162                 });
12163             }
12164             if (this.after && typeof(this.after) == 'object') {
12165                 this.after = Roo.factory(this.after);
12166                 
12167                 inputblock.cn.push({
12168                     tag :'span',
12169                     cls : 'roo-input-after input-group-append  input-group-' +
12170                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12171                 });
12172             }
12173             
12174             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12175                 inputblock.cls += ' has-feedback';
12176                 inputblock.cn.push(feedback);
12177             }
12178         };
12179         var indicator = {
12180             tag : 'i',
12181             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12182             tooltip : 'This field is required'
12183         };
12184         if (this.allowBlank ) {
12185             indicator.style = this.allowBlank ? ' display:none' : '';
12186         }
12187         if (align ==='left' && this.fieldLabel.length) {
12188             
12189             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12190             
12191             cfg.cn = [
12192                 indicator,
12193                 {
12194                     tag: 'label',
12195                     'for' :  id,
12196                     cls : 'control-label col-form-label',
12197                     html : this.fieldLabel
12198
12199                 },
12200                 {
12201                     cls : "", 
12202                     cn: [
12203                         inputblock
12204                     ]
12205                 }
12206             ];
12207             
12208             var labelCfg = cfg.cn[1];
12209             var contentCfg = cfg.cn[2];
12210             
12211             if(this.indicatorpos == 'right'){
12212                 cfg.cn = [
12213                     {
12214                         tag: 'label',
12215                         'for' :  id,
12216                         cls : 'control-label col-form-label',
12217                         cn : [
12218                             {
12219                                 tag : 'span',
12220                                 html : this.fieldLabel
12221                             },
12222                             indicator
12223                         ]
12224                     },
12225                     {
12226                         cls : "",
12227                         cn: [
12228                             inputblock
12229                         ]
12230                     }
12231
12232                 ];
12233                 
12234                 labelCfg = cfg.cn[0];
12235                 contentCfg = cfg.cn[1];
12236             
12237             }
12238             
12239             if(this.labelWidth > 12){
12240                 labelCfg.style = "width: " + this.labelWidth + 'px';
12241             }
12242             
12243             if(this.labelWidth < 13 && this.labelmd == 0){
12244                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12245             }
12246             
12247             if(this.labellg > 0){
12248                 labelCfg.cls += ' col-lg-' + this.labellg;
12249                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12250             }
12251             
12252             if(this.labelmd > 0){
12253                 labelCfg.cls += ' col-md-' + this.labelmd;
12254                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12255             }
12256             
12257             if(this.labelsm > 0){
12258                 labelCfg.cls += ' col-sm-' + this.labelsm;
12259                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12260             }
12261             
12262             if(this.labelxs > 0){
12263                 labelCfg.cls += ' col-xs-' + this.labelxs;
12264                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12265             }
12266             
12267             
12268         } else if ( this.fieldLabel.length) {
12269                 
12270             
12271             
12272             cfg.cn = [
12273                 {
12274                     tag : 'i',
12275                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12276                     tooltip : 'This field is required',
12277                     style : this.allowBlank ? ' display:none' : '' 
12278                 },
12279                 {
12280                     tag: 'label',
12281                    //cls : 'input-group-addon',
12282                     html : this.fieldLabel
12283
12284                 },
12285
12286                inputblock
12287
12288            ];
12289            
12290            if(this.indicatorpos == 'right'){
12291        
12292                 cfg.cn = [
12293                     {
12294                         tag: 'label',
12295                        //cls : 'input-group-addon',
12296                         html : this.fieldLabel
12297
12298                     },
12299                     {
12300                         tag : 'i',
12301                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12302                         tooltip : 'This field is required',
12303                         style : this.allowBlank ? ' display:none' : '' 
12304                     },
12305
12306                    inputblock
12307
12308                ];
12309
12310             }
12311
12312         } else {
12313             
12314             cfg.cn = [
12315
12316                     inputblock
12317
12318             ];
12319                 
12320                 
12321         };
12322         
12323         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12324            cfg.cls += ' navbar-form';
12325         }
12326         
12327         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12328             // on BS4 we do this only if not form 
12329             cfg.cls += ' navbar-form';
12330             cfg.tag = 'li';
12331         }
12332         
12333         return cfg;
12334         
12335     },
12336     /**
12337      * return the real input element.
12338      */
12339     inputEl: function ()
12340     {
12341         return this.el.select('input.form-control',true).first();
12342     },
12343     
12344     tooltipEl : function()
12345     {
12346         return this.inputEl();
12347     },
12348     
12349     indicatorEl : function()
12350     {
12351         if (Roo.bootstrap.version == 4) {
12352             return false; // not enabled in v4 yet.
12353         }
12354         
12355         var indicator = this.el.select('i.roo-required-indicator',true).first();
12356         
12357         if(!indicator){
12358             return false;
12359         }
12360         
12361         return indicator;
12362         
12363     },
12364     
12365     setDisabled : function(v)
12366     {
12367         var i  = this.inputEl().dom;
12368         if (!v) {
12369             i.removeAttribute('disabled');
12370             return;
12371             
12372         }
12373         i.setAttribute('disabled','true');
12374     },
12375     initEvents : function()
12376     {
12377           
12378         this.inputEl().on("keydown" , this.fireKey,  this);
12379         this.inputEl().on("focus", this.onFocus,  this);
12380         this.inputEl().on("blur", this.onBlur,  this);
12381         
12382         this.inputEl().relayEvent('keyup', this);
12383         this.inputEl().relayEvent('paste', this);
12384         
12385         this.indicator = this.indicatorEl();
12386         
12387         if(this.indicator){
12388             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12389         }
12390  
12391         // reference to original value for reset
12392         this.originalValue = this.getValue();
12393         //Roo.form.TextField.superclass.initEvents.call(this);
12394         if(this.validationEvent == 'keyup'){
12395             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12396             this.inputEl().on('keyup', this.filterValidation, this);
12397         }
12398         else if(this.validationEvent !== false){
12399             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12400         }
12401         
12402         if(this.selectOnFocus){
12403             this.on("focus", this.preFocus, this);
12404             
12405         }
12406         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12407             this.inputEl().on("keypress", this.filterKeys, this);
12408         } else {
12409             this.inputEl().relayEvent('keypress', this);
12410         }
12411        /* if(this.grow){
12412             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12413             this.el.on("click", this.autoSize,  this);
12414         }
12415         */
12416         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12417             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12418         }
12419         
12420         if (typeof(this.before) == 'object') {
12421             this.before.render(this.el.select('.roo-input-before',true).first());
12422         }
12423         if (typeof(this.after) == 'object') {
12424             this.after.render(this.el.select('.roo-input-after',true).first());
12425         }
12426         
12427         this.inputEl().on('change', this.onChange, this);
12428         
12429     },
12430     filterValidation : function(e){
12431         if(!e.isNavKeyPress()){
12432             this.validationTask.delay(this.validationDelay);
12433         }
12434     },
12435      /**
12436      * Validates the field value
12437      * @return {Boolean} True if the value is valid, else false
12438      */
12439     validate : function(){
12440         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12441         if(this.disabled || this.validateValue(this.getRawValue())){
12442             this.markValid();
12443             return true;
12444         }
12445         
12446         this.markInvalid();
12447         return false;
12448     },
12449     
12450     
12451     /**
12452      * Validates a value according to the field's validation rules and marks the field as invalid
12453      * if the validation fails
12454      * @param {Mixed} value The value to validate
12455      * @return {Boolean} True if the value is valid, else false
12456      */
12457     validateValue : function(value)
12458     {
12459         if(this.getVisibilityEl().hasClass('hidden')){
12460             return true;
12461         }
12462         
12463         if(value.length < 1)  { // if it's blank
12464             if(this.allowBlank){
12465                 return true;
12466             }
12467             return false;
12468         }
12469         
12470         if(value.length < this.minLength){
12471             return false;
12472         }
12473         if(value.length > this.maxLength){
12474             return false;
12475         }
12476         if(this.vtype){
12477             var vt = Roo.form.VTypes;
12478             if(!vt[this.vtype](value, this)){
12479                 return false;
12480             }
12481         }
12482         if(typeof this.validator == "function"){
12483             var msg = this.validator(value);
12484             if(msg !== true){
12485                 return false;
12486             }
12487             if (typeof(msg) == 'string') {
12488                 this.invalidText = msg;
12489             }
12490         }
12491         
12492         if(this.regex && !this.regex.test(value)){
12493             return false;
12494         }
12495         
12496         return true;
12497     },
12498     
12499      // private
12500     fireKey : function(e){
12501         //Roo.log('field ' + e.getKey());
12502         if(e.isNavKeyPress()){
12503             this.fireEvent("specialkey", this, e);
12504         }
12505     },
12506     focus : function (selectText){
12507         if(this.rendered){
12508             this.inputEl().focus();
12509             if(selectText === true){
12510                 this.inputEl().dom.select();
12511             }
12512         }
12513         return this;
12514     } ,
12515     
12516     onFocus : function(){
12517         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12518            // this.el.addClass(this.focusClass);
12519         }
12520         if(!this.hasFocus){
12521             this.hasFocus = true;
12522             this.startValue = this.getValue();
12523             this.fireEvent("focus", this);
12524         }
12525     },
12526     
12527     beforeBlur : Roo.emptyFn,
12528
12529     
12530     // private
12531     onBlur : function(){
12532         this.beforeBlur();
12533         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12534             //this.el.removeClass(this.focusClass);
12535         }
12536         this.hasFocus = false;
12537         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12538             this.validate();
12539         }
12540         var v = this.getValue();
12541         if(String(v) !== String(this.startValue)){
12542             this.fireEvent('change', this, v, this.startValue);
12543         }
12544         this.fireEvent("blur", this);
12545     },
12546     
12547     onChange : function(e)
12548     {
12549         var v = this.getValue();
12550         if(String(v) !== String(this.startValue)){
12551             this.fireEvent('change', this, v, this.startValue);
12552         }
12553         
12554     },
12555     
12556     /**
12557      * Resets the current field value to the originally loaded value and clears any validation messages
12558      */
12559     reset : function(){
12560         this.setValue(this.originalValue);
12561         this.validate();
12562     },
12563      /**
12564      * Returns the name of the field
12565      * @return {Mixed} name The name field
12566      */
12567     getName: function(){
12568         return this.name;
12569     },
12570      /**
12571      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12572      * @return {Mixed} value The field value
12573      */
12574     getValue : function(){
12575         
12576         var v = this.inputEl().getValue();
12577         
12578         return v;
12579     },
12580     /**
12581      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12582      * @return {Mixed} value The field value
12583      */
12584     getRawValue : function(){
12585         var v = this.inputEl().getValue();
12586         
12587         return v;
12588     },
12589     
12590     /**
12591      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12592      * @param {Mixed} value The value to set
12593      */
12594     setRawValue : function(v){
12595         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12596     },
12597     
12598     selectText : function(start, end){
12599         var v = this.getRawValue();
12600         if(v.length > 0){
12601             start = start === undefined ? 0 : start;
12602             end = end === undefined ? v.length : end;
12603             var d = this.inputEl().dom;
12604             if(d.setSelectionRange){
12605                 d.setSelectionRange(start, end);
12606             }else if(d.createTextRange){
12607                 var range = d.createTextRange();
12608                 range.moveStart("character", start);
12609                 range.moveEnd("character", v.length-end);
12610                 range.select();
12611             }
12612         }
12613     },
12614     
12615     /**
12616      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12617      * @param {Mixed} value The value to set
12618      */
12619     setValue : function(v){
12620         this.value = v;
12621         if(this.rendered){
12622             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12623             this.validate();
12624         }
12625     },
12626     
12627     /*
12628     processValue : function(value){
12629         if(this.stripCharsRe){
12630             var newValue = value.replace(this.stripCharsRe, '');
12631             if(newValue !== value){
12632                 this.setRawValue(newValue);
12633                 return newValue;
12634             }
12635         }
12636         return value;
12637     },
12638   */
12639     preFocus : function(){
12640         
12641         if(this.selectOnFocus){
12642             this.inputEl().dom.select();
12643         }
12644     },
12645     filterKeys : function(e){
12646         var k = e.getKey();
12647         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12648             return;
12649         }
12650         var c = e.getCharCode(), cc = String.fromCharCode(c);
12651         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12652             return;
12653         }
12654         if(!this.maskRe.test(cc)){
12655             e.stopEvent();
12656         }
12657     },
12658      /**
12659      * Clear any invalid styles/messages for this field
12660      */
12661     clearInvalid : function(){
12662         
12663         if(!this.el || this.preventMark){ // not rendered
12664             return;
12665         }
12666         
12667         
12668         this.el.removeClass([this.invalidClass, 'is-invalid']);
12669         
12670         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12671             
12672             var feedback = this.el.select('.form-control-feedback', true).first();
12673             
12674             if(feedback){
12675                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12676             }
12677             
12678         }
12679         
12680         if(this.indicator){
12681             this.indicator.removeClass('visible');
12682             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12683         }
12684         
12685         this.fireEvent('valid', this);
12686     },
12687     
12688      /**
12689      * Mark this field as valid
12690      */
12691     markValid : function()
12692     {
12693         if(!this.el  || this.preventMark){ // not rendered...
12694             return;
12695         }
12696         
12697         this.el.removeClass([this.invalidClass, this.validClass]);
12698         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12699
12700         var feedback = this.el.select('.form-control-feedback', true).first();
12701             
12702         if(feedback){
12703             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12704         }
12705         
12706         if(this.indicator){
12707             this.indicator.removeClass('visible');
12708             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12709         }
12710         
12711         if(this.disabled){
12712             return;
12713         }
12714         
12715            
12716         if(this.allowBlank && !this.getRawValue().length){
12717             return;
12718         }
12719         if (Roo.bootstrap.version == 3) {
12720             this.el.addClass(this.validClass);
12721         } else {
12722             this.inputEl().addClass('is-valid');
12723         }
12724
12725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12726             
12727             var feedback = this.el.select('.form-control-feedback', true).first();
12728             
12729             if(feedback){
12730                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12731                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12732             }
12733             
12734         }
12735         
12736         this.fireEvent('valid', this);
12737     },
12738     
12739      /**
12740      * Mark this field as invalid
12741      * @param {String} msg The validation message
12742      */
12743     markInvalid : function(msg)
12744     {
12745         if(!this.el  || this.preventMark){ // not rendered
12746             return;
12747         }
12748         
12749         this.el.removeClass([this.invalidClass, this.validClass]);
12750         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12751         
12752         var feedback = this.el.select('.form-control-feedback', true).first();
12753             
12754         if(feedback){
12755             this.el.select('.form-control-feedback', true).first().removeClass(
12756                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12757         }
12758
12759         if(this.disabled){
12760             return;
12761         }
12762         
12763         if(this.allowBlank && !this.getRawValue().length){
12764             return;
12765         }
12766         
12767         if(this.indicator){
12768             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12769             this.indicator.addClass('visible');
12770         }
12771         if (Roo.bootstrap.version == 3) {
12772             this.el.addClass(this.invalidClass);
12773         } else {
12774             this.inputEl().addClass('is-invalid');
12775         }
12776         
12777         
12778         
12779         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12780             
12781             var feedback = this.el.select('.form-control-feedback', true).first();
12782             
12783             if(feedback){
12784                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12785                 
12786                 if(this.getValue().length || this.forceFeedback){
12787                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12788                 }
12789                 
12790             }
12791             
12792         }
12793         
12794         this.fireEvent('invalid', this, msg);
12795     },
12796     // private
12797     SafariOnKeyDown : function(event)
12798     {
12799         // this is a workaround for a password hang bug on chrome/ webkit.
12800         if (this.inputEl().dom.type != 'password') {
12801             return;
12802         }
12803         
12804         var isSelectAll = false;
12805         
12806         if(this.inputEl().dom.selectionEnd > 0){
12807             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12808         }
12809         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12810             event.preventDefault();
12811             this.setValue('');
12812             return;
12813         }
12814         
12815         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12816             
12817             event.preventDefault();
12818             // this is very hacky as keydown always get's upper case.
12819             //
12820             var cc = String.fromCharCode(event.getCharCode());
12821             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12822             
12823         }
12824     },
12825     adjustWidth : function(tag, w){
12826         tag = tag.toLowerCase();
12827         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12828             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12829                 if(tag == 'input'){
12830                     return w + 2;
12831                 }
12832                 if(tag == 'textarea'){
12833                     return w-2;
12834                 }
12835             }else if(Roo.isOpera){
12836                 if(tag == 'input'){
12837                     return w + 2;
12838                 }
12839                 if(tag == 'textarea'){
12840                     return w-2;
12841                 }
12842             }
12843         }
12844         return w;
12845     },
12846     
12847     setFieldLabel : function(v)
12848     {
12849         if(!this.rendered){
12850             return;
12851         }
12852         
12853         if(this.indicatorEl()){
12854             var ar = this.el.select('label > span',true);
12855             
12856             if (ar.elements.length) {
12857                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12858                 this.fieldLabel = v;
12859                 return;
12860             }
12861             
12862             var br = this.el.select('label',true);
12863             
12864             if(br.elements.length) {
12865                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12866                 this.fieldLabel = v;
12867                 return;
12868             }
12869             
12870             Roo.log('Cannot Found any of label > span || label in input');
12871             return;
12872         }
12873         
12874         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12875         this.fieldLabel = v;
12876         
12877         
12878     }
12879 });
12880
12881  
12882 /*
12883  * - LGPL
12884  *
12885  * Input
12886  * 
12887  */
12888
12889 /**
12890  * @class Roo.bootstrap.TextArea
12891  * @extends Roo.bootstrap.Input
12892  * Bootstrap TextArea class
12893  * @cfg {Number} cols Specifies the visible width of a text area
12894  * @cfg {Number} rows Specifies the visible number of lines in a text area
12895  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12896  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12897  * @cfg {string} html text
12898  * 
12899  * @constructor
12900  * Create a new TextArea
12901  * @param {Object} config The config object
12902  */
12903
12904 Roo.bootstrap.TextArea = function(config){
12905     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12906    
12907 };
12908
12909 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12910      
12911     cols : false,
12912     rows : 5,
12913     readOnly : false,
12914     warp : 'soft',
12915     resize : false,
12916     value: false,
12917     html: false,
12918     
12919     getAutoCreate : function(){
12920         
12921         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12922         
12923         var id = Roo.id();
12924         
12925         var cfg = {};
12926         
12927         if(this.inputType != 'hidden'){
12928             cfg.cls = 'form-group' //input-group
12929         }
12930         
12931         var input =  {
12932             tag: 'textarea',
12933             id : id,
12934             warp : this.warp,
12935             rows : this.rows,
12936             value : this.value || '',
12937             html: this.html || '',
12938             cls : 'form-control',
12939             placeholder : this.placeholder || '' 
12940             
12941         };
12942         
12943         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12944             input.maxLength = this.maxLength;
12945         }
12946         
12947         if(this.resize){
12948             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12949         }
12950         
12951         if(this.cols){
12952             input.cols = this.cols;
12953         }
12954         
12955         if (this.readOnly) {
12956             input.readonly = true;
12957         }
12958         
12959         if (this.name) {
12960             input.name = this.name;
12961         }
12962         
12963         if (this.size) {
12964             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12965         }
12966         
12967         var settings=this;
12968         ['xs','sm','md','lg'].map(function(size){
12969             if (settings[size]) {
12970                 cfg.cls += ' col-' + size + '-' + settings[size];
12971             }
12972         });
12973         
12974         var inputblock = input;
12975         
12976         if(this.hasFeedback && !this.allowBlank){
12977             
12978             var feedback = {
12979                 tag: 'span',
12980                 cls: 'glyphicon form-control-feedback'
12981             };
12982
12983             inputblock = {
12984                 cls : 'has-feedback',
12985                 cn :  [
12986                     input,
12987                     feedback
12988                 ] 
12989             };  
12990         }
12991         
12992         
12993         if (this.before || this.after) {
12994             
12995             inputblock = {
12996                 cls : 'input-group',
12997                 cn :  [] 
12998             };
12999             if (this.before) {
13000                 inputblock.cn.push({
13001                     tag :'span',
13002                     cls : 'input-group-addon',
13003                     html : this.before
13004                 });
13005             }
13006             
13007             inputblock.cn.push(input);
13008             
13009             if(this.hasFeedback && !this.allowBlank){
13010                 inputblock.cls += ' has-feedback';
13011                 inputblock.cn.push(feedback);
13012             }
13013             
13014             if (this.after) {
13015                 inputblock.cn.push({
13016                     tag :'span',
13017                     cls : 'input-group-addon',
13018                     html : this.after
13019                 });
13020             }
13021             
13022         }
13023         
13024         if (align ==='left' && this.fieldLabel.length) {
13025             cfg.cn = [
13026                 {
13027                     tag: 'label',
13028                     'for' :  id,
13029                     cls : 'control-label',
13030                     html : this.fieldLabel
13031                 },
13032                 {
13033                     cls : "",
13034                     cn: [
13035                         inputblock
13036                     ]
13037                 }
13038
13039             ];
13040             
13041             if(this.labelWidth > 12){
13042                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13043             }
13044
13045             if(this.labelWidth < 13 && this.labelmd == 0){
13046                 this.labelmd = this.labelWidth;
13047             }
13048
13049             if(this.labellg > 0){
13050                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13051                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13052             }
13053
13054             if(this.labelmd > 0){
13055                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13056                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13057             }
13058
13059             if(this.labelsm > 0){
13060                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13061                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13062             }
13063
13064             if(this.labelxs > 0){
13065                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13066                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13067             }
13068             
13069         } else if ( this.fieldLabel.length) {
13070             cfg.cn = [
13071
13072                {
13073                    tag: 'label',
13074                    //cls : 'input-group-addon',
13075                    html : this.fieldLabel
13076
13077                },
13078
13079                inputblock
13080
13081            ];
13082
13083         } else {
13084
13085             cfg.cn = [
13086
13087                 inputblock
13088
13089             ];
13090                 
13091         }
13092         
13093         if (this.disabled) {
13094             input.disabled=true;
13095         }
13096         
13097         return cfg;
13098         
13099     },
13100     /**
13101      * return the real textarea element.
13102      */
13103     inputEl: function ()
13104     {
13105         return this.el.select('textarea.form-control',true).first();
13106     },
13107     
13108     /**
13109      * Clear any invalid styles/messages for this field
13110      */
13111     clearInvalid : function()
13112     {
13113         
13114         if(!this.el || this.preventMark){ // not rendered
13115             return;
13116         }
13117         
13118         var label = this.el.select('label', true).first();
13119         var icon = this.el.select('i.fa-star', true).first();
13120         
13121         if(label && icon){
13122             icon.remove();
13123         }
13124         this.el.removeClass( this.validClass);
13125         this.inputEl().removeClass('is-invalid');
13126          
13127         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13128             
13129             var feedback = this.el.select('.form-control-feedback', true).first();
13130             
13131             if(feedback){
13132                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13133             }
13134             
13135         }
13136         
13137         this.fireEvent('valid', this);
13138     },
13139     
13140      /**
13141      * Mark this field as valid
13142      */
13143     markValid : function()
13144     {
13145         if(!this.el  || this.preventMark){ // not rendered
13146             return;
13147         }
13148         
13149         this.el.removeClass([this.invalidClass, this.validClass]);
13150         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13151         
13152         var feedback = this.el.select('.form-control-feedback', true).first();
13153             
13154         if(feedback){
13155             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13156         }
13157
13158         if(this.disabled || this.allowBlank){
13159             return;
13160         }
13161         
13162         var label = this.el.select('label', true).first();
13163         var icon = this.el.select('i.fa-star', true).first();
13164         
13165         if(label && icon){
13166             icon.remove();
13167         }
13168         if (Roo.bootstrap.version == 3) {
13169             this.el.addClass(this.validClass);
13170         } else {
13171             this.inputEl().addClass('is-valid');
13172         }
13173         
13174         
13175         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13176             
13177             var feedback = this.el.select('.form-control-feedback', true).first();
13178             
13179             if(feedback){
13180                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13181                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13182             }
13183             
13184         }
13185         
13186         this.fireEvent('valid', this);
13187     },
13188     
13189      /**
13190      * Mark this field as invalid
13191      * @param {String} msg The validation message
13192      */
13193     markInvalid : function(msg)
13194     {
13195         if(!this.el  || this.preventMark){ // not rendered
13196             return;
13197         }
13198         
13199         this.el.removeClass([this.invalidClass, this.validClass]);
13200         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13201         
13202         var feedback = this.el.select('.form-control-feedback', true).first();
13203             
13204         if(feedback){
13205             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13206         }
13207
13208         if(this.disabled || this.allowBlank){
13209             return;
13210         }
13211         
13212         var label = this.el.select('label', true).first();
13213         var icon = this.el.select('i.fa-star', true).first();
13214         
13215         if(!this.getValue().length && label && !icon){
13216             this.el.createChild({
13217                 tag : 'i',
13218                 cls : 'text-danger fa fa-lg fa-star',
13219                 tooltip : 'This field is required',
13220                 style : 'margin-right:5px;'
13221             }, label, true);
13222         }
13223         
13224         if (Roo.bootstrap.version == 3) {
13225             this.el.addClass(this.invalidClass);
13226         } else {
13227             this.inputEl().addClass('is-invalid');
13228         }
13229         
13230         // fixme ... this may be depricated need to test..
13231         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13232             
13233             var feedback = this.el.select('.form-control-feedback', true).first();
13234             
13235             if(feedback){
13236                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13237                 
13238                 if(this.getValue().length || this.forceFeedback){
13239                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13240                 }
13241                 
13242             }
13243             
13244         }
13245         
13246         this.fireEvent('invalid', this, msg);
13247     }
13248 });
13249
13250  
13251 /*
13252  * - LGPL
13253  *
13254  * trigger field - base class for combo..
13255  * 
13256  */
13257  
13258 /**
13259  * @class Roo.bootstrap.TriggerField
13260  * @extends Roo.bootstrap.Input
13261  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13262  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13263  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13264  * for which you can provide a custom implementation.  For example:
13265  * <pre><code>
13266 var trigger = new Roo.bootstrap.TriggerField();
13267 trigger.onTriggerClick = myTriggerFn;
13268 trigger.applyTo('my-field');
13269 </code></pre>
13270  *
13271  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13272  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13273  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13274  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13275  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13276
13277  * @constructor
13278  * Create a new TriggerField.
13279  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13280  * to the base TextField)
13281  */
13282 Roo.bootstrap.TriggerField = function(config){
13283     this.mimicing = false;
13284     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13285 };
13286
13287 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13288     /**
13289      * @cfg {String} triggerClass A CSS class to apply to the trigger
13290      */
13291      /**
13292      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13293      */
13294     hideTrigger:false,
13295
13296     /**
13297      * @cfg {Boolean} removable (true|false) special filter default false
13298      */
13299     removable : false,
13300     
13301     /** @cfg {Boolean} grow @hide */
13302     /** @cfg {Number} growMin @hide */
13303     /** @cfg {Number} growMax @hide */
13304
13305     /**
13306      * @hide 
13307      * @method
13308      */
13309     autoSize: Roo.emptyFn,
13310     // private
13311     monitorTab : true,
13312     // private
13313     deferHeight : true,
13314
13315     
13316     actionMode : 'wrap',
13317     
13318     caret : false,
13319     
13320     
13321     getAutoCreate : function(){
13322        
13323         var align = this.labelAlign || this.parentLabelAlign();
13324         
13325         var id = Roo.id();
13326         
13327         var cfg = {
13328             cls: 'form-group' //input-group
13329         };
13330         
13331         
13332         var input =  {
13333             tag: 'input',
13334             id : id,
13335             type : this.inputType,
13336             cls : 'form-control',
13337             autocomplete: 'new-password',
13338             placeholder : this.placeholder || '' 
13339             
13340         };
13341         if (this.name) {
13342             input.name = this.name;
13343         }
13344         if (this.size) {
13345             input.cls += ' input-' + this.size;
13346         }
13347         
13348         if (this.disabled) {
13349             input.disabled=true;
13350         }
13351         
13352         var inputblock = input;
13353         
13354         if(this.hasFeedback && !this.allowBlank){
13355             
13356             var feedback = {
13357                 tag: 'span',
13358                 cls: 'glyphicon form-control-feedback'
13359             };
13360             
13361             if(this.removable && !this.editable  ){
13362                 inputblock = {
13363                     cls : 'has-feedback',
13364                     cn :  [
13365                         inputblock,
13366                         {
13367                             tag: 'button',
13368                             html : 'x',
13369                             cls : 'roo-combo-removable-btn close'
13370                         },
13371                         feedback
13372                     ] 
13373                 };
13374             } else {
13375                 inputblock = {
13376                     cls : 'has-feedback',
13377                     cn :  [
13378                         inputblock,
13379                         feedback
13380                     ] 
13381                 };
13382             }
13383
13384         } else {
13385             if(this.removable && !this.editable ){
13386                 inputblock = {
13387                     cls : 'roo-removable',
13388                     cn :  [
13389                         inputblock,
13390                         {
13391                             tag: 'button',
13392                             html : 'x',
13393                             cls : 'roo-combo-removable-btn close'
13394                         }
13395                     ] 
13396                 };
13397             }
13398         }
13399         
13400         if (this.before || this.after) {
13401             
13402             inputblock = {
13403                 cls : 'input-group',
13404                 cn :  [] 
13405             };
13406             if (this.before) {
13407                 inputblock.cn.push({
13408                     tag :'span',
13409                     cls : 'input-group-addon input-group-prepend input-group-text',
13410                     html : this.before
13411                 });
13412             }
13413             
13414             inputblock.cn.push(input);
13415             
13416             if(this.hasFeedback && !this.allowBlank){
13417                 inputblock.cls += ' has-feedback';
13418                 inputblock.cn.push(feedback);
13419             }
13420             
13421             if (this.after) {
13422                 inputblock.cn.push({
13423                     tag :'span',
13424                     cls : 'input-group-addon input-group-append input-group-text',
13425                     html : this.after
13426                 });
13427             }
13428             
13429         };
13430         
13431       
13432         
13433         var ibwrap = inputblock;
13434         
13435         if(this.multiple){
13436             ibwrap = {
13437                 tag: 'ul',
13438                 cls: 'roo-select2-choices',
13439                 cn:[
13440                     {
13441                         tag: 'li',
13442                         cls: 'roo-select2-search-field',
13443                         cn: [
13444
13445                             inputblock
13446                         ]
13447                     }
13448                 ]
13449             };
13450                 
13451         }
13452         
13453         var combobox = {
13454             cls: 'roo-select2-container input-group',
13455             cn: [
13456                  {
13457                     tag: 'input',
13458                     type : 'hidden',
13459                     cls: 'form-hidden-field'
13460                 },
13461                 ibwrap
13462             ]
13463         };
13464         
13465         if(!this.multiple && this.showToggleBtn){
13466             
13467             var caret = {
13468                         tag: 'span',
13469                         cls: 'caret'
13470              };
13471             if (this.caret != false) {
13472                 caret = {
13473                      tag: 'i',
13474                      cls: 'fa fa-' + this.caret
13475                 };
13476                 
13477             }
13478             
13479             combobox.cn.push({
13480                 tag :'span',
13481                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13482                 cn : [
13483                     Roo.bootstrap.version == 3 ? caret : '',
13484                     {
13485                         tag: 'span',
13486                         cls: 'combobox-clear',
13487                         cn  : [
13488                             {
13489                                 tag : 'i',
13490                                 cls: 'icon-remove'
13491                             }
13492                         ]
13493                     }
13494                 ]
13495
13496             })
13497         }
13498         
13499         if(this.multiple){
13500             combobox.cls += ' roo-select2-container-multi';
13501         }
13502          var indicator = {
13503             tag : 'i',
13504             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13505             tooltip : 'This field is required'
13506         };
13507         if (Roo.bootstrap.version == 4) {
13508             indicator = {
13509                 tag : 'i',
13510                 style : 'display:none'
13511             };
13512         }
13513         
13514         
13515         if (align ==='left' && this.fieldLabel.length) {
13516             
13517             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13518
13519             cfg.cn = [
13520                 indicator,
13521                 {
13522                     tag: 'label',
13523                     'for' :  id,
13524                     cls : 'control-label',
13525                     html : this.fieldLabel
13526
13527                 },
13528                 {
13529                     cls : "", 
13530                     cn: [
13531                         combobox
13532                     ]
13533                 }
13534
13535             ];
13536             
13537             var labelCfg = cfg.cn[1];
13538             var contentCfg = cfg.cn[2];
13539             
13540             if(this.indicatorpos == 'right'){
13541                 cfg.cn = [
13542                     {
13543                         tag: 'label',
13544                         'for' :  id,
13545                         cls : 'control-label',
13546                         cn : [
13547                             {
13548                                 tag : 'span',
13549                                 html : this.fieldLabel
13550                             },
13551                             indicator
13552                         ]
13553                     },
13554                     {
13555                         cls : "", 
13556                         cn: [
13557                             combobox
13558                         ]
13559                     }
13560
13561                 ];
13562                 
13563                 labelCfg = cfg.cn[0];
13564                 contentCfg = cfg.cn[1];
13565             }
13566             
13567             if(this.labelWidth > 12){
13568                 labelCfg.style = "width: " + this.labelWidth + 'px';
13569             }
13570             
13571             if(this.labelWidth < 13 && this.labelmd == 0){
13572                 this.labelmd = this.labelWidth;
13573             }
13574             
13575             if(this.labellg > 0){
13576                 labelCfg.cls += ' col-lg-' + this.labellg;
13577                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13578             }
13579             
13580             if(this.labelmd > 0){
13581                 labelCfg.cls += ' col-md-' + this.labelmd;
13582                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13583             }
13584             
13585             if(this.labelsm > 0){
13586                 labelCfg.cls += ' col-sm-' + this.labelsm;
13587                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13588             }
13589             
13590             if(this.labelxs > 0){
13591                 labelCfg.cls += ' col-xs-' + this.labelxs;
13592                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13593             }
13594             
13595         } else if ( this.fieldLabel.length) {
13596 //                Roo.log(" label");
13597             cfg.cn = [
13598                 indicator,
13599                {
13600                    tag: 'label',
13601                    //cls : 'input-group-addon',
13602                    html : this.fieldLabel
13603
13604                },
13605
13606                combobox
13607
13608             ];
13609             
13610             if(this.indicatorpos == 'right'){
13611                 
13612                 cfg.cn = [
13613                     {
13614                        tag: 'label',
13615                        cn : [
13616                            {
13617                                tag : 'span',
13618                                html : this.fieldLabel
13619                            },
13620                            indicator
13621                        ]
13622
13623                     },
13624                     combobox
13625
13626                 ];
13627
13628             }
13629
13630         } else {
13631             
13632 //                Roo.log(" no label && no align");
13633                 cfg = combobox
13634                      
13635                 
13636         }
13637         
13638         var settings=this;
13639         ['xs','sm','md','lg'].map(function(size){
13640             if (settings[size]) {
13641                 cfg.cls += ' col-' + size + '-' + settings[size];
13642             }
13643         });
13644         
13645         return cfg;
13646         
13647     },
13648     
13649     
13650     
13651     // private
13652     onResize : function(w, h){
13653 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13654 //        if(typeof w == 'number'){
13655 //            var x = w - this.trigger.getWidth();
13656 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13657 //            this.trigger.setStyle('left', x+'px');
13658 //        }
13659     },
13660
13661     // private
13662     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13663
13664     // private
13665     getResizeEl : function(){
13666         return this.inputEl();
13667     },
13668
13669     // private
13670     getPositionEl : function(){
13671         return this.inputEl();
13672     },
13673
13674     // private
13675     alignErrorIcon : function(){
13676         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13677     },
13678
13679     // private
13680     initEvents : function(){
13681         
13682         this.createList();
13683         
13684         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13685         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13686         if(!this.multiple && this.showToggleBtn){
13687             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13688             if(this.hideTrigger){
13689                 this.trigger.setDisplayed(false);
13690             }
13691             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13692         }
13693         
13694         if(this.multiple){
13695             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13696         }
13697         
13698         if(this.removable && !this.editable && !this.tickable){
13699             var close = this.closeTriggerEl();
13700             
13701             if(close){
13702                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13703                 close.on('click', this.removeBtnClick, this, close);
13704             }
13705         }
13706         
13707         //this.trigger.addClassOnOver('x-form-trigger-over');
13708         //this.trigger.addClassOnClick('x-form-trigger-click');
13709         
13710         //if(!this.width){
13711         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13712         //}
13713     },
13714     
13715     closeTriggerEl : function()
13716     {
13717         var close = this.el.select('.roo-combo-removable-btn', true).first();
13718         return close ? close : false;
13719     },
13720     
13721     removeBtnClick : function(e, h, el)
13722     {
13723         e.preventDefault();
13724         
13725         if(this.fireEvent("remove", this) !== false){
13726             this.reset();
13727             this.fireEvent("afterremove", this)
13728         }
13729     },
13730     
13731     createList : function()
13732     {
13733         this.list = Roo.get(document.body).createChild({
13734             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13735             cls: 'typeahead typeahead-long dropdown-menu shadow',
13736             style: 'display:none'
13737         });
13738         
13739         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13740         
13741     },
13742
13743     // private
13744     initTrigger : function(){
13745        
13746     },
13747
13748     // private
13749     onDestroy : function(){
13750         if(this.trigger){
13751             this.trigger.removeAllListeners();
13752           //  this.trigger.remove();
13753         }
13754         //if(this.wrap){
13755         //    this.wrap.remove();
13756         //}
13757         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13758     },
13759
13760     // private
13761     onFocus : function(){
13762         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13763         /*
13764         if(!this.mimicing){
13765             this.wrap.addClass('x-trigger-wrap-focus');
13766             this.mimicing = true;
13767             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13768             if(this.monitorTab){
13769                 this.el.on("keydown", this.checkTab, this);
13770             }
13771         }
13772         */
13773     },
13774
13775     // private
13776     checkTab : function(e){
13777         if(e.getKey() == e.TAB){
13778             this.triggerBlur();
13779         }
13780     },
13781
13782     // private
13783     onBlur : function(){
13784         // do nothing
13785     },
13786
13787     // private
13788     mimicBlur : function(e, t){
13789         /*
13790         if(!this.wrap.contains(t) && this.validateBlur()){
13791             this.triggerBlur();
13792         }
13793         */
13794     },
13795
13796     // private
13797     triggerBlur : function(){
13798         this.mimicing = false;
13799         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13800         if(this.monitorTab){
13801             this.el.un("keydown", this.checkTab, this);
13802         }
13803         //this.wrap.removeClass('x-trigger-wrap-focus');
13804         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13805     },
13806
13807     // private
13808     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13809     validateBlur : function(e, t){
13810         return true;
13811     },
13812
13813     // private
13814     onDisable : function(){
13815         this.inputEl().dom.disabled = true;
13816         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13817         //if(this.wrap){
13818         //    this.wrap.addClass('x-item-disabled');
13819         //}
13820     },
13821
13822     // private
13823     onEnable : function(){
13824         this.inputEl().dom.disabled = false;
13825         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13826         //if(this.wrap){
13827         //    this.el.removeClass('x-item-disabled');
13828         //}
13829     },
13830
13831     // private
13832     onShow : function(){
13833         var ae = this.getActionEl();
13834         
13835         if(ae){
13836             ae.dom.style.display = '';
13837             ae.dom.style.visibility = 'visible';
13838         }
13839     },
13840
13841     // private
13842     
13843     onHide : function(){
13844         var ae = this.getActionEl();
13845         ae.dom.style.display = 'none';
13846     },
13847
13848     /**
13849      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13850      * by an implementing function.
13851      * @method
13852      * @param {EventObject} e
13853      */
13854     onTriggerClick : Roo.emptyFn
13855 });
13856  
13857 /*
13858 * Licence: LGPL
13859 */
13860
13861 /**
13862  * @class Roo.bootstrap.CardUploader
13863  * @extends Roo.bootstrap.Button
13864  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13865  * @cfg {Number} errorTimeout default 3000
13866  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13867  * @cfg {Array}  html The button text.
13868
13869  *
13870  * @constructor
13871  * Create a new CardUploader
13872  * @param {Object} config The config object
13873  */
13874
13875 Roo.bootstrap.CardUploader = function(config){
13876     
13877  
13878     
13879     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13880     
13881     
13882     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13883         return r.data.id
13884      });
13885     
13886      this.addEvents({
13887          // raw events
13888         /**
13889          * @event preview
13890          * When a image is clicked on - and needs to display a slideshow or similar..
13891          * @param {Roo.bootstrap.Card} this
13892          * @param {Object} The image information data 
13893          *
13894          */
13895         'preview' : true,
13896          /**
13897          * @event download
13898          * When a the download link is clicked
13899          * @param {Roo.bootstrap.Card} this
13900          * @param {Object} The image information data  contains 
13901          */
13902         'download' : true
13903         
13904     });
13905 };
13906  
13907 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13908     
13909      
13910     errorTimeout : 3000,
13911      
13912     images : false,
13913    
13914     fileCollection : false,
13915     allowBlank : true,
13916     
13917     getAutoCreate : function()
13918     {
13919         
13920         var cfg =  {
13921             cls :'form-group' ,
13922             cn : [
13923                
13924                 {
13925                     tag: 'label',
13926                    //cls : 'input-group-addon',
13927                     html : this.fieldLabel
13928
13929                 },
13930
13931                 {
13932                     tag: 'input',
13933                     type : 'hidden',
13934                     name : this.name,
13935                     value : this.value,
13936                     cls : 'd-none  form-control'
13937                 },
13938                 
13939                 {
13940                     tag: 'input',
13941                     multiple : 'multiple',
13942                     type : 'file',
13943                     cls : 'd-none  roo-card-upload-selector'
13944                 },
13945                 
13946                 {
13947                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13948                 },
13949                 {
13950                     cls : 'card-columns roo-card-uploader-container'
13951                 }
13952
13953             ]
13954         };
13955            
13956          
13957         return cfg;
13958     },
13959     
13960     getChildContainer : function() /// what children are added to.
13961     {
13962         return this.containerEl;
13963     },
13964    
13965     getButtonContainer : function() /// what children are added to.
13966     {
13967         return this.el.select(".roo-card-uploader-button-container").first();
13968     },
13969    
13970     initEvents : function()
13971     {
13972         
13973         Roo.bootstrap.Input.prototype.initEvents.call(this);
13974         
13975         var t = this;
13976         this.addxtype({
13977             xns: Roo.bootstrap,
13978
13979             xtype : 'Button',
13980             container_method : 'getButtonContainer' ,            
13981             html :  this.html, // fix changable?
13982             cls : 'w-100 ',
13983             listeners : {
13984                 'click' : function(btn, e) {
13985                     t.onClick(e);
13986                 }
13987             }
13988         });
13989         
13990         
13991         
13992         
13993         this.urlAPI = (window.createObjectURL && window) || 
13994                                 (window.URL && URL.revokeObjectURL && URL) || 
13995                                 (window.webkitURL && webkitURL);
13996                         
13997          
13998          
13999          
14000         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14001         
14002         this.selectorEl.on('change', this.onFileSelected, this);
14003         if (this.images) {
14004             var t = this;
14005             this.images.forEach(function(img) {
14006                 t.addCard(img)
14007             });
14008             this.images = false;
14009         }
14010         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14011          
14012        
14013     },
14014     
14015    
14016     onClick : function(e)
14017     {
14018         e.preventDefault();
14019          
14020         this.selectorEl.dom.click();
14021          
14022     },
14023     
14024     onFileSelected : function(e)
14025     {
14026         e.preventDefault();
14027         
14028         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14029             return;
14030         }
14031         
14032         Roo.each(this.selectorEl.dom.files, function(file){    
14033             this.addFile(file);
14034         }, this);
14035          
14036     },
14037     
14038       
14039     
14040       
14041     
14042     addFile : function(file)
14043     {
14044            
14045         if(typeof(file) === 'string'){
14046             throw "Add file by name?"; // should not happen
14047             return;
14048         }
14049         
14050         if(!file || !this.urlAPI){
14051             return;
14052         }
14053         
14054         // file;
14055         // file.type;
14056         
14057         var _this = this;
14058         
14059         
14060         var url = _this.urlAPI.createObjectURL( file);
14061            
14062         this.addCard({
14063             id : Roo.bootstrap.CardUploader.ID--,
14064             is_uploaded : false,
14065             src : url,
14066             srcfile : file,
14067             title : file.name,
14068             mimetype : file.type,
14069             preview : false,
14070             is_deleted : 0
14071         });
14072         
14073     },
14074     
14075     /**
14076      * addCard - add an Attachment to the uploader
14077      * @param data - the data about the image to upload
14078      *
14079      * {
14080           id : 123
14081           title : "Title of file",
14082           is_uploaded : false,
14083           src : "http://.....",
14084           srcfile : { the File upload object },
14085           mimetype : file.type,
14086           preview : false,
14087           is_deleted : 0
14088           .. any other data...
14089         }
14090      *
14091      * 
14092     */
14093     
14094     addCard : function (data)
14095     {
14096         // hidden input element?
14097         // if the file is not an image...
14098         //then we need to use something other that and header_image
14099         var t = this;
14100         //   remove.....
14101         var footer = [
14102             {
14103                 xns : Roo.bootstrap,
14104                 xtype : 'CardFooter',
14105                  items: [
14106                     {
14107                         xns : Roo.bootstrap,
14108                         xtype : 'Element',
14109                         cls : 'd-flex',
14110                         items : [
14111                             
14112                             {
14113                                 xns : Roo.bootstrap,
14114                                 xtype : 'Button',
14115                                 html : String.format("<small>{0}</small>", data.title),
14116                                 cls : 'col-10 text-left',
14117                                 size: 'sm',
14118                                 weight: 'link',
14119                                 fa : 'download',
14120                                 listeners : {
14121                                     click : function() {
14122                                      
14123                                         t.fireEvent( "download", t, data );
14124                                     }
14125                                 }
14126                             },
14127                           
14128                             {
14129                                 xns : Roo.bootstrap,
14130                                 xtype : 'Button',
14131                                 style: 'max-height: 28px; ',
14132                                 size : 'sm',
14133                                 weight: 'danger',
14134                                 cls : 'col-2',
14135                                 fa : 'times',
14136                                 listeners : {
14137                                     click : function() {
14138                                         t.removeCard(data.id)
14139                                     }
14140                                 }
14141                             }
14142                         ]
14143                     }
14144                     
14145                 ] 
14146             }
14147             
14148         ];
14149         
14150         var cn = this.addxtype(
14151             {
14152                  
14153                 xns : Roo.bootstrap,
14154                 xtype : 'Card',
14155                 closeable : true,
14156                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14157                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14158                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14159                 data : data,
14160                 html : false,
14161                  
14162                 items : footer,
14163                 initEvents : function() {
14164                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14165                     var card = this;
14166                     this.imgEl = this.el.select('.card-img-top').first();
14167                     if (this.imgEl) {
14168                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14169                         this.imgEl.set({ 'pointer' : 'cursor' });
14170                                   
14171                     }
14172                     this.getCardFooter().addClass('p-1');
14173                     
14174                   
14175                 }
14176                 
14177             }
14178         );
14179         // dont' really need ot update items.
14180         // this.items.push(cn);
14181         this.fileCollection.add(cn);
14182         
14183         if (!data.srcfile) {
14184             this.updateInput();
14185             return;
14186         }
14187             
14188         var _t = this;
14189         var reader = new FileReader();
14190         reader.addEventListener("load", function() {  
14191             data.srcdata =  reader.result;
14192             _t.updateInput();
14193         });
14194         reader.readAsDataURL(data.srcfile);
14195         
14196         
14197         
14198     },
14199     removeCard : function(id)
14200     {
14201         
14202         var card  = this.fileCollection.get(id);
14203         card.data.is_deleted = 1;
14204         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14205         //this.fileCollection.remove(card);
14206         //this.items = this.items.filter(function(e) { return e != card });
14207         // dont' really need ot update items.
14208         card.el.dom.parentNode.removeChild(card.el.dom);
14209         this.updateInput();
14210
14211         
14212     },
14213     reset: function()
14214     {
14215         this.fileCollection.each(function(card) {
14216             if (card.el.dom && card.el.dom.parentNode) {
14217                 card.el.dom.parentNode.removeChild(card.el.dom);
14218             }
14219         });
14220         this.fileCollection.clear();
14221         this.updateInput();
14222     },
14223     
14224     updateInput : function()
14225     {
14226          var data = [];
14227         this.fileCollection.each(function(e) {
14228             data.push(e.data);
14229             
14230         });
14231         this.inputEl().dom.value = JSON.stringify(data);
14232         
14233         
14234         
14235     }
14236     
14237     
14238 });
14239
14240
14241 Roo.bootstrap.CardUploader.ID = -1;/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251
14252
14253 /**
14254  * @class Roo.data.SortTypes
14255  * @singleton
14256  * Defines the default sorting (casting?) comparison functions used when sorting data.
14257  */
14258 Roo.data.SortTypes = {
14259     /**
14260      * Default sort that does nothing
14261      * @param {Mixed} s The value being converted
14262      * @return {Mixed} The comparison value
14263      */
14264     none : function(s){
14265         return s;
14266     },
14267     
14268     /**
14269      * The regular expression used to strip tags
14270      * @type {RegExp}
14271      * @property
14272      */
14273     stripTagsRE : /<\/?[^>]+>/gi,
14274     
14275     /**
14276      * Strips all HTML tags to sort on text only
14277      * @param {Mixed} s The value being converted
14278      * @return {String} The comparison value
14279      */
14280     asText : function(s){
14281         return String(s).replace(this.stripTagsRE, "");
14282     },
14283     
14284     /**
14285      * Strips all HTML tags to sort on text only - Case insensitive
14286      * @param {Mixed} s The value being converted
14287      * @return {String} The comparison value
14288      */
14289     asUCText : function(s){
14290         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14291     },
14292     
14293     /**
14294      * Case insensitive string
14295      * @param {Mixed} s The value being converted
14296      * @return {String} The comparison value
14297      */
14298     asUCString : function(s) {
14299         return String(s).toUpperCase();
14300     },
14301     
14302     /**
14303      * Date sorting
14304      * @param {Mixed} s The value being converted
14305      * @return {Number} The comparison value
14306      */
14307     asDate : function(s) {
14308         if(!s){
14309             return 0;
14310         }
14311         if(s instanceof Date){
14312             return s.getTime();
14313         }
14314         return Date.parse(String(s));
14315     },
14316     
14317     /**
14318      * Float sorting
14319      * @param {Mixed} s The value being converted
14320      * @return {Float} The comparison value
14321      */
14322     asFloat : function(s) {
14323         var val = parseFloat(String(s).replace(/,/g, ""));
14324         if(isNaN(val)) {
14325             val = 0;
14326         }
14327         return val;
14328     },
14329     
14330     /**
14331      * Integer sorting
14332      * @param {Mixed} s The value being converted
14333      * @return {Number} The comparison value
14334      */
14335     asInt : function(s) {
14336         var val = parseInt(String(s).replace(/,/g, ""));
14337         if(isNaN(val)) {
14338             val = 0;
14339         }
14340         return val;
14341     }
14342 };/*
14343  * Based on:
14344  * Ext JS Library 1.1.1
14345  * Copyright(c) 2006-2007, Ext JS, LLC.
14346  *
14347  * Originally Released Under LGPL - original licence link has changed is not relivant.
14348  *
14349  * Fork - LGPL
14350  * <script type="text/javascript">
14351  */
14352
14353 /**
14354 * @class Roo.data.Record
14355  * Instances of this class encapsulate both record <em>definition</em> information, and record
14356  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14357  * to access Records cached in an {@link Roo.data.Store} object.<br>
14358  * <p>
14359  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14360  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14361  * objects.<br>
14362  * <p>
14363  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14364  * @constructor
14365  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14366  * {@link #create}. The parameters are the same.
14367  * @param {Array} data An associative Array of data values keyed by the field name.
14368  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14369  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14370  * not specified an integer id is generated.
14371  */
14372 Roo.data.Record = function(data, id){
14373     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14374     this.data = data;
14375 };
14376
14377 /**
14378  * Generate a constructor for a specific record layout.
14379  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14380  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14381  * Each field definition object may contain the following properties: <ul>
14382  * <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,
14383  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14384  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14385  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14386  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14387  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14388  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14389  * this may be omitted.</p></li>
14390  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14391  * <ul><li>auto (Default, implies no conversion)</li>
14392  * <li>string</li>
14393  * <li>int</li>
14394  * <li>float</li>
14395  * <li>boolean</li>
14396  * <li>date</li></ul></p></li>
14397  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14398  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14399  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14400  * by the Reader into an object that will be stored in the Record. It is passed the
14401  * following parameters:<ul>
14402  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14403  * </ul></p></li>
14404  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14405  * </ul>
14406  * <br>usage:<br><pre><code>
14407 var TopicRecord = Roo.data.Record.create(
14408     {name: 'title', mapping: 'topic_title'},
14409     {name: 'author', mapping: 'username'},
14410     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14411     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14412     {name: 'lastPoster', mapping: 'user2'},
14413     {name: 'excerpt', mapping: 'post_text'}
14414 );
14415
14416 var myNewRecord = new TopicRecord({
14417     title: 'Do my job please',
14418     author: 'noobie',
14419     totalPosts: 1,
14420     lastPost: new Date(),
14421     lastPoster: 'Animal',
14422     excerpt: 'No way dude!'
14423 });
14424 myStore.add(myNewRecord);
14425 </code></pre>
14426  * @method create
14427  * @static
14428  */
14429 Roo.data.Record.create = function(o){
14430     var f = function(){
14431         f.superclass.constructor.apply(this, arguments);
14432     };
14433     Roo.extend(f, Roo.data.Record);
14434     var p = f.prototype;
14435     p.fields = new Roo.util.MixedCollection(false, function(field){
14436         return field.name;
14437     });
14438     for(var i = 0, len = o.length; i < len; i++){
14439         p.fields.add(new Roo.data.Field(o[i]));
14440     }
14441     f.getField = function(name){
14442         return p.fields.get(name);  
14443     };
14444     return f;
14445 };
14446
14447 Roo.data.Record.AUTO_ID = 1000;
14448 Roo.data.Record.EDIT = 'edit';
14449 Roo.data.Record.REJECT = 'reject';
14450 Roo.data.Record.COMMIT = 'commit';
14451
14452 Roo.data.Record.prototype = {
14453     /**
14454      * Readonly flag - true if this record has been modified.
14455      * @type Boolean
14456      */
14457     dirty : false,
14458     editing : false,
14459     error: null,
14460     modified: null,
14461
14462     // private
14463     join : function(store){
14464         this.store = store;
14465     },
14466
14467     /**
14468      * Set the named field to the specified value.
14469      * @param {String} name The name of the field to set.
14470      * @param {Object} value The value to set the field to.
14471      */
14472     set : function(name, value){
14473         if(this.data[name] == value){
14474             return;
14475         }
14476         this.dirty = true;
14477         if(!this.modified){
14478             this.modified = {};
14479         }
14480         if(typeof this.modified[name] == 'undefined'){
14481             this.modified[name] = this.data[name];
14482         }
14483         this.data[name] = value;
14484         if(!this.editing && this.store){
14485             this.store.afterEdit(this);
14486         }       
14487     },
14488
14489     /**
14490      * Get the value of the named field.
14491      * @param {String} name The name of the field to get the value of.
14492      * @return {Object} The value of the field.
14493      */
14494     get : function(name){
14495         return this.data[name]; 
14496     },
14497
14498     // private
14499     beginEdit : function(){
14500         this.editing = true;
14501         this.modified = {}; 
14502     },
14503
14504     // private
14505     cancelEdit : function(){
14506         this.editing = false;
14507         delete this.modified;
14508     },
14509
14510     // private
14511     endEdit : function(){
14512         this.editing = false;
14513         if(this.dirty && this.store){
14514             this.store.afterEdit(this);
14515         }
14516     },
14517
14518     /**
14519      * Usually called by the {@link Roo.data.Store} which owns the Record.
14520      * Rejects all changes made to the Record since either creation, or the last commit operation.
14521      * Modified fields are reverted to their original values.
14522      * <p>
14523      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14524      * of reject operations.
14525      */
14526     reject : function(){
14527         var m = this.modified;
14528         for(var n in m){
14529             if(typeof m[n] != "function"){
14530                 this.data[n] = m[n];
14531             }
14532         }
14533         this.dirty = false;
14534         delete this.modified;
14535         this.editing = false;
14536         if(this.store){
14537             this.store.afterReject(this);
14538         }
14539     },
14540
14541     /**
14542      * Usually called by the {@link Roo.data.Store} which owns the Record.
14543      * Commits all changes made to the Record since either creation, or the last commit operation.
14544      * <p>
14545      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14546      * of commit operations.
14547      */
14548     commit : function(){
14549         this.dirty = false;
14550         delete this.modified;
14551         this.editing = false;
14552         if(this.store){
14553             this.store.afterCommit(this);
14554         }
14555     },
14556
14557     // private
14558     hasError : function(){
14559         return this.error != null;
14560     },
14561
14562     // private
14563     clearError : function(){
14564         this.error = null;
14565     },
14566
14567     /**
14568      * Creates a copy of this record.
14569      * @param {String} id (optional) A new record id if you don't want to use this record's id
14570      * @return {Record}
14571      */
14572     copy : function(newId) {
14573         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14574     }
14575 };/*
14576  * Based on:
14577  * Ext JS Library 1.1.1
14578  * Copyright(c) 2006-2007, Ext JS, LLC.
14579  *
14580  * Originally Released Under LGPL - original licence link has changed is not relivant.
14581  *
14582  * Fork - LGPL
14583  * <script type="text/javascript">
14584  */
14585
14586
14587
14588 /**
14589  * @class Roo.data.Store
14590  * @extends Roo.util.Observable
14591  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14592  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14593  * <p>
14594  * 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
14595  * has no knowledge of the format of the data returned by the Proxy.<br>
14596  * <p>
14597  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14598  * instances from the data object. These records are cached and made available through accessor functions.
14599  * @constructor
14600  * Creates a new Store.
14601  * @param {Object} config A config object containing the objects needed for the Store to access data,
14602  * and read the data into Records.
14603  */
14604 Roo.data.Store = function(config){
14605     this.data = new Roo.util.MixedCollection(false);
14606     this.data.getKey = function(o){
14607         return o.id;
14608     };
14609     this.baseParams = {};
14610     // private
14611     this.paramNames = {
14612         "start" : "start",
14613         "limit" : "limit",
14614         "sort" : "sort",
14615         "dir" : "dir",
14616         "multisort" : "_multisort"
14617     };
14618
14619     if(config && config.data){
14620         this.inlineData = config.data;
14621         delete config.data;
14622     }
14623
14624     Roo.apply(this, config);
14625     
14626     if(this.reader){ // reader passed
14627         this.reader = Roo.factory(this.reader, Roo.data);
14628         this.reader.xmodule = this.xmodule || false;
14629         if(!this.recordType){
14630             this.recordType = this.reader.recordType;
14631         }
14632         if(this.reader.onMetaChange){
14633             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14634         }
14635     }
14636
14637     if(this.recordType){
14638         this.fields = this.recordType.prototype.fields;
14639     }
14640     this.modified = [];
14641
14642     this.addEvents({
14643         /**
14644          * @event datachanged
14645          * Fires when the data cache has changed, and a widget which is using this Store
14646          * as a Record cache should refresh its view.
14647          * @param {Store} this
14648          */
14649         datachanged : true,
14650         /**
14651          * @event metachange
14652          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14653          * @param {Store} this
14654          * @param {Object} meta The JSON metadata
14655          */
14656         metachange : true,
14657         /**
14658          * @event add
14659          * Fires when Records have been added to the Store
14660          * @param {Store} this
14661          * @param {Roo.data.Record[]} records The array of Records added
14662          * @param {Number} index The index at which the record(s) were added
14663          */
14664         add : true,
14665         /**
14666          * @event remove
14667          * Fires when a Record has been removed from the Store
14668          * @param {Store} this
14669          * @param {Roo.data.Record} record The Record that was removed
14670          * @param {Number} index The index at which the record was removed
14671          */
14672         remove : true,
14673         /**
14674          * @event update
14675          * Fires when a Record has been updated
14676          * @param {Store} this
14677          * @param {Roo.data.Record} record The Record that was updated
14678          * @param {String} operation The update operation being performed.  Value may be one of:
14679          * <pre><code>
14680  Roo.data.Record.EDIT
14681  Roo.data.Record.REJECT
14682  Roo.data.Record.COMMIT
14683          * </code></pre>
14684          */
14685         update : true,
14686         /**
14687          * @event clear
14688          * Fires when the data cache has been cleared.
14689          * @param {Store} this
14690          */
14691         clear : true,
14692         /**
14693          * @event beforeload
14694          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14695          * the load action will be canceled.
14696          * @param {Store} this
14697          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14698          */
14699         beforeload : true,
14700         /**
14701          * @event beforeloadadd
14702          * Fires after a new set of Records has been loaded.
14703          * @param {Store} this
14704          * @param {Roo.data.Record[]} records The Records that were loaded
14705          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14706          */
14707         beforeloadadd : true,
14708         /**
14709          * @event load
14710          * Fires after a new set of Records has been loaded, before they are added to the store.
14711          * @param {Store} this
14712          * @param {Roo.data.Record[]} records The Records that were loaded
14713          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14714          * @params {Object} return from reader
14715          */
14716         load : true,
14717         /**
14718          * @event loadexception
14719          * Fires if an exception occurs in the Proxy during loading.
14720          * Called with the signature of the Proxy's "loadexception" event.
14721          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14722          * 
14723          * @param {Proxy} 
14724          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14725          * @param {Object} load options 
14726          * @param {Object} jsonData from your request (normally this contains the Exception)
14727          */
14728         loadexception : true
14729     });
14730     
14731     if(this.proxy){
14732         this.proxy = Roo.factory(this.proxy, Roo.data);
14733         this.proxy.xmodule = this.xmodule || false;
14734         this.relayEvents(this.proxy,  ["loadexception"]);
14735     }
14736     this.sortToggle = {};
14737     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14738
14739     Roo.data.Store.superclass.constructor.call(this);
14740
14741     if(this.inlineData){
14742         this.loadData(this.inlineData);
14743         delete this.inlineData;
14744     }
14745 };
14746
14747 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14748      /**
14749     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14750     * without a remote query - used by combo/forms at present.
14751     */
14752     
14753     /**
14754     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14755     */
14756     /**
14757     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14758     */
14759     /**
14760     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14761     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14762     */
14763     /**
14764     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14765     * on any HTTP request
14766     */
14767     /**
14768     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14769     */
14770     /**
14771     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14772     */
14773     multiSort: false,
14774     /**
14775     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14776     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14777     */
14778     remoteSort : false,
14779
14780     /**
14781     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14782      * loaded or when a record is removed. (defaults to false).
14783     */
14784     pruneModifiedRecords : false,
14785
14786     // private
14787     lastOptions : null,
14788
14789     /**
14790      * Add Records to the Store and fires the add event.
14791      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14792      */
14793     add : function(records){
14794         records = [].concat(records);
14795         for(var i = 0, len = records.length; i < len; i++){
14796             records[i].join(this);
14797         }
14798         var index = this.data.length;
14799         this.data.addAll(records);
14800         this.fireEvent("add", this, records, index);
14801     },
14802
14803     /**
14804      * Remove a Record from the Store and fires the remove event.
14805      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14806      */
14807     remove : function(record){
14808         var index = this.data.indexOf(record);
14809         this.data.removeAt(index);
14810  
14811         if(this.pruneModifiedRecords){
14812             this.modified.remove(record);
14813         }
14814         this.fireEvent("remove", this, record, index);
14815     },
14816
14817     /**
14818      * Remove all Records from the Store and fires the clear event.
14819      */
14820     removeAll : function(){
14821         this.data.clear();
14822         if(this.pruneModifiedRecords){
14823             this.modified = [];
14824         }
14825         this.fireEvent("clear", this);
14826     },
14827
14828     /**
14829      * Inserts Records to the Store at the given index and fires the add event.
14830      * @param {Number} index The start index at which to insert the passed Records.
14831      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14832      */
14833     insert : function(index, records){
14834         records = [].concat(records);
14835         for(var i = 0, len = records.length; i < len; i++){
14836             this.data.insert(index, records[i]);
14837             records[i].join(this);
14838         }
14839         this.fireEvent("add", this, records, index);
14840     },
14841
14842     /**
14843      * Get the index within the cache of the passed Record.
14844      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14845      * @return {Number} The index of the passed Record. Returns -1 if not found.
14846      */
14847     indexOf : function(record){
14848         return this.data.indexOf(record);
14849     },
14850
14851     /**
14852      * Get the index within the cache of the Record with the passed id.
14853      * @param {String} id The id of the Record to find.
14854      * @return {Number} The index of the Record. Returns -1 if not found.
14855      */
14856     indexOfId : function(id){
14857         return this.data.indexOfKey(id);
14858     },
14859
14860     /**
14861      * Get the Record with the specified id.
14862      * @param {String} id The id of the Record to find.
14863      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14864      */
14865     getById : function(id){
14866         return this.data.key(id);
14867     },
14868
14869     /**
14870      * Get the Record at the specified index.
14871      * @param {Number} index The index of the Record to find.
14872      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14873      */
14874     getAt : function(index){
14875         return this.data.itemAt(index);
14876     },
14877
14878     /**
14879      * Returns a range of Records between specified indices.
14880      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14881      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14882      * @return {Roo.data.Record[]} An array of Records
14883      */
14884     getRange : function(start, end){
14885         return this.data.getRange(start, end);
14886     },
14887
14888     // private
14889     storeOptions : function(o){
14890         o = Roo.apply({}, o);
14891         delete o.callback;
14892         delete o.scope;
14893         this.lastOptions = o;
14894     },
14895
14896     /**
14897      * Loads the Record cache from the configured Proxy using the configured Reader.
14898      * <p>
14899      * If using remote paging, then the first load call must specify the <em>start</em>
14900      * and <em>limit</em> properties in the options.params property to establish the initial
14901      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14902      * <p>
14903      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14904      * and this call will return before the new data has been loaded. Perform any post-processing
14905      * in a callback function, or in a "load" event handler.</strong>
14906      * <p>
14907      * @param {Object} options An object containing properties which control loading options:<ul>
14908      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14909      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14910      * passed the following arguments:<ul>
14911      * <li>r : Roo.data.Record[]</li>
14912      * <li>options: Options object from the load call</li>
14913      * <li>success: Boolean success indicator</li></ul></li>
14914      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14915      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14916      * </ul>
14917      */
14918     load : function(options){
14919         options = options || {};
14920         if(this.fireEvent("beforeload", this, options) !== false){
14921             this.storeOptions(options);
14922             var p = Roo.apply(options.params || {}, this.baseParams);
14923             // if meta was not loaded from remote source.. try requesting it.
14924             if (!this.reader.metaFromRemote) {
14925                 p._requestMeta = 1;
14926             }
14927             if(this.sortInfo && this.remoteSort){
14928                 var pn = this.paramNames;
14929                 p[pn["sort"]] = this.sortInfo.field;
14930                 p[pn["dir"]] = this.sortInfo.direction;
14931             }
14932             if (this.multiSort) {
14933                 var pn = this.paramNames;
14934                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14935             }
14936             
14937             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14938         }
14939     },
14940
14941     /**
14942      * Reloads the Record cache from the configured Proxy using the configured Reader and
14943      * the options from the last load operation performed.
14944      * @param {Object} options (optional) An object containing properties which may override the options
14945      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14946      * the most recently used options are reused).
14947      */
14948     reload : function(options){
14949         this.load(Roo.applyIf(options||{}, this.lastOptions));
14950     },
14951
14952     // private
14953     // Called as a callback by the Reader during a load operation.
14954     loadRecords : function(o, options, success){
14955         if(!o || success === false){
14956             if(success !== false){
14957                 this.fireEvent("load", this, [], options, o);
14958             }
14959             if(options.callback){
14960                 options.callback.call(options.scope || this, [], options, false);
14961             }
14962             return;
14963         }
14964         // if data returned failure - throw an exception.
14965         if (o.success === false) {
14966             // show a message if no listener is registered.
14967             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14968                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14969             }
14970             // loadmask wil be hooked into this..
14971             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14972             return;
14973         }
14974         var r = o.records, t = o.totalRecords || r.length;
14975         
14976         this.fireEvent("beforeloadadd", this, r, options, o);
14977         
14978         if(!options || options.add !== true){
14979             if(this.pruneModifiedRecords){
14980                 this.modified = [];
14981             }
14982             for(var i = 0, len = r.length; i < len; i++){
14983                 r[i].join(this);
14984             }
14985             if(this.snapshot){
14986                 this.data = this.snapshot;
14987                 delete this.snapshot;
14988             }
14989             this.data.clear();
14990             this.data.addAll(r);
14991             this.totalLength = t;
14992             this.applySort();
14993             this.fireEvent("datachanged", this);
14994         }else{
14995             this.totalLength = Math.max(t, this.data.length+r.length);
14996             this.add(r);
14997         }
14998         
14999         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15000                 
15001             var e = new Roo.data.Record({});
15002
15003             e.set(this.parent.displayField, this.parent.emptyTitle);
15004             e.set(this.parent.valueField, '');
15005
15006             this.insert(0, e);
15007         }
15008             
15009         this.fireEvent("load", this, r, options, o);
15010         if(options.callback){
15011             options.callback.call(options.scope || this, r, options, true);
15012         }
15013     },
15014
15015
15016     /**
15017      * Loads data from a passed data block. A Reader which understands the format of the data
15018      * must have been configured in the constructor.
15019      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15020      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15021      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15022      */
15023     loadData : function(o, append){
15024         var r = this.reader.readRecords(o);
15025         this.loadRecords(r, {add: append}, true);
15026     },
15027     
15028      /**
15029      * using 'cn' the nested child reader read the child array into it's child stores.
15030      * @param {Object} rec The record with a 'children array
15031      */
15032     loadDataFromChildren : function(rec)
15033     {
15034         this.loadData(this.reader.toLoadData(rec));
15035     },
15036     
15037
15038     /**
15039      * Gets the number of cached records.
15040      * <p>
15041      * <em>If using paging, this may not be the total size of the dataset. If the data object
15042      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15043      * the data set size</em>
15044      */
15045     getCount : function(){
15046         return this.data.length || 0;
15047     },
15048
15049     /**
15050      * Gets the total number of records in the dataset as returned by the server.
15051      * <p>
15052      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15053      * the dataset size</em>
15054      */
15055     getTotalCount : function(){
15056         return this.totalLength || 0;
15057     },
15058
15059     /**
15060      * Returns the sort state of the Store as an object with two properties:
15061      * <pre><code>
15062  field {String} The name of the field by which the Records are sorted
15063  direction {String} The sort order, "ASC" or "DESC"
15064      * </code></pre>
15065      */
15066     getSortState : function(){
15067         return this.sortInfo;
15068     },
15069
15070     // private
15071     applySort : function(){
15072         if(this.sortInfo && !this.remoteSort){
15073             var s = this.sortInfo, f = s.field;
15074             var st = this.fields.get(f).sortType;
15075             var fn = function(r1, r2){
15076                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15077                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15078             };
15079             this.data.sort(s.direction, fn);
15080             if(this.snapshot && this.snapshot != this.data){
15081                 this.snapshot.sort(s.direction, fn);
15082             }
15083         }
15084     },
15085
15086     /**
15087      * Sets the default sort column and order to be used by the next load operation.
15088      * @param {String} fieldName The name of the field to sort by.
15089      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15090      */
15091     setDefaultSort : function(field, dir){
15092         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15093     },
15094
15095     /**
15096      * Sort the Records.
15097      * If remote sorting is used, the sort is performed on the server, and the cache is
15098      * reloaded. If local sorting is used, the cache is sorted internally.
15099      * @param {String} fieldName The name of the field to sort by.
15100      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15101      */
15102     sort : function(fieldName, dir){
15103         var f = this.fields.get(fieldName);
15104         if(!dir){
15105             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15106             
15107             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15108                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15109             }else{
15110                 dir = f.sortDir;
15111             }
15112         }
15113         this.sortToggle[f.name] = dir;
15114         this.sortInfo = {field: f.name, direction: dir};
15115         if(!this.remoteSort){
15116             this.applySort();
15117             this.fireEvent("datachanged", this);
15118         }else{
15119             this.load(this.lastOptions);
15120         }
15121     },
15122
15123     /**
15124      * Calls the specified function for each of the Records in the cache.
15125      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15126      * Returning <em>false</em> aborts and exits the iteration.
15127      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15128      */
15129     each : function(fn, scope){
15130         this.data.each(fn, scope);
15131     },
15132
15133     /**
15134      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15135      * (e.g., during paging).
15136      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15137      */
15138     getModifiedRecords : function(){
15139         return this.modified;
15140     },
15141
15142     // private
15143     createFilterFn : function(property, value, anyMatch){
15144         if(!value.exec){ // not a regex
15145             value = String(value);
15146             if(value.length == 0){
15147                 return false;
15148             }
15149             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15150         }
15151         return function(r){
15152             return value.test(r.data[property]);
15153         };
15154     },
15155
15156     /**
15157      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15158      * @param {String} property A field on your records
15159      * @param {Number} start The record index to start at (defaults to 0)
15160      * @param {Number} end The last record index to include (defaults to length - 1)
15161      * @return {Number} The sum
15162      */
15163     sum : function(property, start, end){
15164         var rs = this.data.items, v = 0;
15165         start = start || 0;
15166         end = (end || end === 0) ? end : rs.length-1;
15167
15168         for(var i = start; i <= end; i++){
15169             v += (rs[i].data[property] || 0);
15170         }
15171         return v;
15172     },
15173
15174     /**
15175      * Filter the records by a specified property.
15176      * @param {String} field A field on your records
15177      * @param {String/RegExp} value Either a string that the field
15178      * should start with or a RegExp to test against the field
15179      * @param {Boolean} anyMatch True to match any part not just the beginning
15180      */
15181     filter : function(property, value, anyMatch){
15182         var fn = this.createFilterFn(property, value, anyMatch);
15183         return fn ? this.filterBy(fn) : this.clearFilter();
15184     },
15185
15186     /**
15187      * Filter by a function. The specified function will be called with each
15188      * record in this data source. If the function returns true the record is included,
15189      * otherwise it is filtered.
15190      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15191      * @param {Object} scope (optional) The scope of the function (defaults to this)
15192      */
15193     filterBy : function(fn, scope){
15194         this.snapshot = this.snapshot || this.data;
15195         this.data = this.queryBy(fn, scope||this);
15196         this.fireEvent("datachanged", this);
15197     },
15198
15199     /**
15200      * Query the records by a specified property.
15201      * @param {String} field A field on your records
15202      * @param {String/RegExp} value Either a string that the field
15203      * should start with or a RegExp to test against the field
15204      * @param {Boolean} anyMatch True to match any part not just the beginning
15205      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15206      */
15207     query : function(property, value, anyMatch){
15208         var fn = this.createFilterFn(property, value, anyMatch);
15209         return fn ? this.queryBy(fn) : this.data.clone();
15210     },
15211
15212     /**
15213      * Query by a function. The specified function will be called with each
15214      * record in this data source. If the function returns true the record is included
15215      * in the results.
15216      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15217      * @param {Object} scope (optional) The scope of the function (defaults to this)
15218       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15219      **/
15220     queryBy : function(fn, scope){
15221         var data = this.snapshot || this.data;
15222         return data.filterBy(fn, scope||this);
15223     },
15224
15225     /**
15226      * Collects unique values for a particular dataIndex from this store.
15227      * @param {String} dataIndex The property to collect
15228      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15229      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15230      * @return {Array} An array of the unique values
15231      **/
15232     collect : function(dataIndex, allowNull, bypassFilter){
15233         var d = (bypassFilter === true && this.snapshot) ?
15234                 this.snapshot.items : this.data.items;
15235         var v, sv, r = [], l = {};
15236         for(var i = 0, len = d.length; i < len; i++){
15237             v = d[i].data[dataIndex];
15238             sv = String(v);
15239             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15240                 l[sv] = true;
15241                 r[r.length] = v;
15242             }
15243         }
15244         return r;
15245     },
15246
15247     /**
15248      * Revert to a view of the Record cache with no filtering applied.
15249      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15250      */
15251     clearFilter : function(suppressEvent){
15252         if(this.snapshot && this.snapshot != this.data){
15253             this.data = this.snapshot;
15254             delete this.snapshot;
15255             if(suppressEvent !== true){
15256                 this.fireEvent("datachanged", this);
15257             }
15258         }
15259     },
15260
15261     // private
15262     afterEdit : function(record){
15263         if(this.modified.indexOf(record) == -1){
15264             this.modified.push(record);
15265         }
15266         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15267     },
15268     
15269     // private
15270     afterReject : function(record){
15271         this.modified.remove(record);
15272         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15273     },
15274
15275     // private
15276     afterCommit : function(record){
15277         this.modified.remove(record);
15278         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15279     },
15280
15281     /**
15282      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15283      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15284      */
15285     commitChanges : function(){
15286         var m = this.modified.slice(0);
15287         this.modified = [];
15288         for(var i = 0, len = m.length; i < len; i++){
15289             m[i].commit();
15290         }
15291     },
15292
15293     /**
15294      * Cancel outstanding changes on all changed records.
15295      */
15296     rejectChanges : function(){
15297         var m = this.modified.slice(0);
15298         this.modified = [];
15299         for(var i = 0, len = m.length; i < len; i++){
15300             m[i].reject();
15301         }
15302     },
15303
15304     onMetaChange : function(meta, rtype, o){
15305         this.recordType = rtype;
15306         this.fields = rtype.prototype.fields;
15307         delete this.snapshot;
15308         this.sortInfo = meta.sortInfo || this.sortInfo;
15309         this.modified = [];
15310         this.fireEvent('metachange', this, this.reader.meta);
15311     },
15312     
15313     moveIndex : function(data, type)
15314     {
15315         var index = this.indexOf(data);
15316         
15317         var newIndex = index + type;
15318         
15319         this.remove(data);
15320         
15321         this.insert(newIndex, data);
15322         
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334
15335 /**
15336  * @class Roo.data.SimpleStore
15337  * @extends Roo.data.Store
15338  * Small helper class to make creating Stores from Array data easier.
15339  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15340  * @cfg {Array} fields An array of field definition objects, or field name strings.
15341  * @cfg {Object} an existing reader (eg. copied from another store)
15342  * @cfg {Array} data The multi-dimensional array of data
15343  * @constructor
15344  * @param {Object} config
15345  */
15346 Roo.data.SimpleStore = function(config)
15347 {
15348     Roo.data.SimpleStore.superclass.constructor.call(this, {
15349         isLocal : true,
15350         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15351                 id: config.id
15352             },
15353             Roo.data.Record.create(config.fields)
15354         ),
15355         proxy : new Roo.data.MemoryProxy(config.data)
15356     });
15357     this.load();
15358 };
15359 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15360  * Based on:
15361  * Ext JS Library 1.1.1
15362  * Copyright(c) 2006-2007, Ext JS, LLC.
15363  *
15364  * Originally Released Under LGPL - original licence link has changed is not relivant.
15365  *
15366  * Fork - LGPL
15367  * <script type="text/javascript">
15368  */
15369
15370 /**
15371 /**
15372  * @extends Roo.data.Store
15373  * @class Roo.data.JsonStore
15374  * Small helper class to make creating Stores for JSON data easier. <br/>
15375 <pre><code>
15376 var store = new Roo.data.JsonStore({
15377     url: 'get-images.php',
15378     root: 'images',
15379     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15380 });
15381 </code></pre>
15382  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15383  * JsonReader and HttpProxy (unless inline data is provided).</b>
15384  * @cfg {Array} fields An array of field definition objects, or field name strings.
15385  * @constructor
15386  * @param {Object} config
15387  */
15388 Roo.data.JsonStore = function(c){
15389     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15390         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15391         reader: new Roo.data.JsonReader(c, c.fields)
15392     }));
15393 };
15394 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15395  * Based on:
15396  * Ext JS Library 1.1.1
15397  * Copyright(c) 2006-2007, Ext JS, LLC.
15398  *
15399  * Originally Released Under LGPL - original licence link has changed is not relivant.
15400  *
15401  * Fork - LGPL
15402  * <script type="text/javascript">
15403  */
15404
15405  
15406 Roo.data.Field = function(config){
15407     if(typeof config == "string"){
15408         config = {name: config};
15409     }
15410     Roo.apply(this, config);
15411     
15412     if(!this.type){
15413         this.type = "auto";
15414     }
15415     
15416     var st = Roo.data.SortTypes;
15417     // named sortTypes are supported, here we look them up
15418     if(typeof this.sortType == "string"){
15419         this.sortType = st[this.sortType];
15420     }
15421     
15422     // set default sortType for strings and dates
15423     if(!this.sortType){
15424         switch(this.type){
15425             case "string":
15426                 this.sortType = st.asUCString;
15427                 break;
15428             case "date":
15429                 this.sortType = st.asDate;
15430                 break;
15431             default:
15432                 this.sortType = st.none;
15433         }
15434     }
15435
15436     // define once
15437     var stripRe = /[\$,%]/g;
15438
15439     // prebuilt conversion function for this field, instead of
15440     // switching every time we're reading a value
15441     if(!this.convert){
15442         var cv, dateFormat = this.dateFormat;
15443         switch(this.type){
15444             case "":
15445             case "auto":
15446             case undefined:
15447                 cv = function(v){ return v; };
15448                 break;
15449             case "string":
15450                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15451                 break;
15452             case "int":
15453                 cv = function(v){
15454                     return v !== undefined && v !== null && v !== '' ?
15455                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15456                     };
15457                 break;
15458             case "float":
15459                 cv = function(v){
15460                     return v !== undefined && v !== null && v !== '' ?
15461                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15462                     };
15463                 break;
15464             case "bool":
15465             case "boolean":
15466                 cv = function(v){ return v === true || v === "true" || v == 1; };
15467                 break;
15468             case "date":
15469                 cv = function(v){
15470                     if(!v){
15471                         return '';
15472                     }
15473                     if(v instanceof Date){
15474                         return v;
15475                     }
15476                     if(dateFormat){
15477                         if(dateFormat == "timestamp"){
15478                             return new Date(v*1000);
15479                         }
15480                         return Date.parseDate(v, dateFormat);
15481                     }
15482                     var parsed = Date.parse(v);
15483                     return parsed ? new Date(parsed) : null;
15484                 };
15485              break;
15486             
15487         }
15488         this.convert = cv;
15489     }
15490 };
15491
15492 Roo.data.Field.prototype = {
15493     dateFormat: null,
15494     defaultValue: "",
15495     mapping: null,
15496     sortType : null,
15497     sortDir : "ASC"
15498 };/*
15499  * Based on:
15500  * Ext JS Library 1.1.1
15501  * Copyright(c) 2006-2007, Ext JS, LLC.
15502  *
15503  * Originally Released Under LGPL - original licence link has changed is not relivant.
15504  *
15505  * Fork - LGPL
15506  * <script type="text/javascript">
15507  */
15508  
15509 // Base class for reading structured data from a data source.  This class is intended to be
15510 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15511
15512 /**
15513  * @class Roo.data.DataReader
15514  * Base class for reading structured data from a data source.  This class is intended to be
15515  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15516  */
15517
15518 Roo.data.DataReader = function(meta, recordType){
15519     
15520     this.meta = meta;
15521     
15522     this.recordType = recordType instanceof Array ? 
15523         Roo.data.Record.create(recordType) : recordType;
15524 };
15525
15526 Roo.data.DataReader.prototype = {
15527     
15528     
15529     readerType : 'Data',
15530      /**
15531      * Create an empty record
15532      * @param {Object} data (optional) - overlay some values
15533      * @return {Roo.data.Record} record created.
15534      */
15535     newRow :  function(d) {
15536         var da =  {};
15537         this.recordType.prototype.fields.each(function(c) {
15538             switch( c.type) {
15539                 case 'int' : da[c.name] = 0; break;
15540                 case 'date' : da[c.name] = new Date(); break;
15541                 case 'float' : da[c.name] = 0.0; break;
15542                 case 'boolean' : da[c.name] = false; break;
15543                 default : da[c.name] = ""; break;
15544             }
15545             
15546         });
15547         return new this.recordType(Roo.apply(da, d));
15548     }
15549     
15550     
15551 };/*
15552  * Based on:
15553  * Ext JS Library 1.1.1
15554  * Copyright(c) 2006-2007, Ext JS, LLC.
15555  *
15556  * Originally Released Under LGPL - original licence link has changed is not relivant.
15557  *
15558  * Fork - LGPL
15559  * <script type="text/javascript">
15560  */
15561
15562 /**
15563  * @class Roo.data.DataProxy
15564  * @extends Roo.data.Observable
15565  * This class is an abstract base class for implementations which provide retrieval of
15566  * unformatted data objects.<br>
15567  * <p>
15568  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15569  * (of the appropriate type which knows how to parse the data object) to provide a block of
15570  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15571  * <p>
15572  * Custom implementations must implement the load method as described in
15573  * {@link Roo.data.HttpProxy#load}.
15574  */
15575 Roo.data.DataProxy = function(){
15576     this.addEvents({
15577         /**
15578          * @event beforeload
15579          * Fires before a network request is made to retrieve a data object.
15580          * @param {Object} This DataProxy object.
15581          * @param {Object} params The params parameter to the load function.
15582          */
15583         beforeload : true,
15584         /**
15585          * @event load
15586          * Fires before the load method's callback is called.
15587          * @param {Object} This DataProxy object.
15588          * @param {Object} o The data object.
15589          * @param {Object} arg The callback argument object passed to the load function.
15590          */
15591         load : true,
15592         /**
15593          * @event loadexception
15594          * Fires if an Exception occurs during data retrieval.
15595          * @param {Object} This DataProxy object.
15596          * @param {Object} o The data object.
15597          * @param {Object} arg The callback argument object passed to the load function.
15598          * @param {Object} e The Exception.
15599          */
15600         loadexception : true
15601     });
15602     Roo.data.DataProxy.superclass.constructor.call(this);
15603 };
15604
15605 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15606
15607     /**
15608      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15609      */
15610 /*
15611  * Based on:
15612  * Ext JS Library 1.1.1
15613  * Copyright(c) 2006-2007, Ext JS, LLC.
15614  *
15615  * Originally Released Under LGPL - original licence link has changed is not relivant.
15616  *
15617  * Fork - LGPL
15618  * <script type="text/javascript">
15619  */
15620 /**
15621  * @class Roo.data.MemoryProxy
15622  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15623  * to the Reader when its load method is called.
15624  * @constructor
15625  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15626  */
15627 Roo.data.MemoryProxy = function(data){
15628     if (data.data) {
15629         data = data.data;
15630     }
15631     Roo.data.MemoryProxy.superclass.constructor.call(this);
15632     this.data = data;
15633 };
15634
15635 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15636     
15637     /**
15638      * Load data from the requested source (in this case an in-memory
15639      * data object passed to the constructor), read the data object into
15640      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15641      * process that block using the passed callback.
15642      * @param {Object} params This parameter is not used by the MemoryProxy class.
15643      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15644      * object into a block of Roo.data.Records.
15645      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15646      * The function must be passed <ul>
15647      * <li>The Record block object</li>
15648      * <li>The "arg" argument from the load function</li>
15649      * <li>A boolean success indicator</li>
15650      * </ul>
15651      * @param {Object} scope The scope in which to call the callback
15652      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15653      */
15654     load : function(params, reader, callback, scope, arg){
15655         params = params || {};
15656         var result;
15657         try {
15658             result = reader.readRecords(params.data ? params.data :this.data);
15659         }catch(e){
15660             this.fireEvent("loadexception", this, arg, null, e);
15661             callback.call(scope, null, arg, false);
15662             return;
15663         }
15664         callback.call(scope, result, arg, true);
15665     },
15666     
15667     // private
15668     update : function(params, records){
15669         
15670     }
15671 });/*
15672  * Based on:
15673  * Ext JS Library 1.1.1
15674  * Copyright(c) 2006-2007, Ext JS, LLC.
15675  *
15676  * Originally Released Under LGPL - original licence link has changed is not relivant.
15677  *
15678  * Fork - LGPL
15679  * <script type="text/javascript">
15680  */
15681 /**
15682  * @class Roo.data.HttpProxy
15683  * @extends Roo.data.DataProxy
15684  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15685  * configured to reference a certain URL.<br><br>
15686  * <p>
15687  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15688  * from which the running page was served.<br><br>
15689  * <p>
15690  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15691  * <p>
15692  * Be aware that to enable the browser to parse an XML document, the server must set
15693  * the Content-Type header in the HTTP response to "text/xml".
15694  * @constructor
15695  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15696  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15697  * will be used to make the request.
15698  */
15699 Roo.data.HttpProxy = function(conn){
15700     Roo.data.HttpProxy.superclass.constructor.call(this);
15701     // is conn a conn config or a real conn?
15702     this.conn = conn;
15703     this.useAjax = !conn || !conn.events;
15704   
15705 };
15706
15707 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15708     // thse are take from connection...
15709     
15710     /**
15711      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15712      */
15713     /**
15714      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15715      * extra parameters to each request made by this object. (defaults to undefined)
15716      */
15717     /**
15718      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15719      *  to each request made by this object. (defaults to undefined)
15720      */
15721     /**
15722      * @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)
15723      */
15724     /**
15725      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15726      */
15727      /**
15728      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15729      * @type Boolean
15730      */
15731   
15732
15733     /**
15734      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15735      * @type Boolean
15736      */
15737     /**
15738      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15739      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15740      * a finer-grained basis than the DataProxy events.
15741      */
15742     getConnection : function(){
15743         return this.useAjax ? Roo.Ajax : this.conn;
15744     },
15745
15746     /**
15747      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15748      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15749      * process that block using the passed callback.
15750      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15751      * for the request to the remote server.
15752      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15753      * object into a block of Roo.data.Records.
15754      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15755      * The function must be passed <ul>
15756      * <li>The Record block object</li>
15757      * <li>The "arg" argument from the load function</li>
15758      * <li>A boolean success indicator</li>
15759      * </ul>
15760      * @param {Object} scope The scope in which to call the callback
15761      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15762      */
15763     load : function(params, reader, callback, scope, arg){
15764         if(this.fireEvent("beforeload", this, params) !== false){
15765             var  o = {
15766                 params : params || {},
15767                 request: {
15768                     callback : callback,
15769                     scope : scope,
15770                     arg : arg
15771                 },
15772                 reader: reader,
15773                 callback : this.loadResponse,
15774                 scope: this
15775             };
15776             if(this.useAjax){
15777                 Roo.applyIf(o, this.conn);
15778                 if(this.activeRequest){
15779                     Roo.Ajax.abort(this.activeRequest);
15780                 }
15781                 this.activeRequest = Roo.Ajax.request(o);
15782             }else{
15783                 this.conn.request(o);
15784             }
15785         }else{
15786             callback.call(scope||this, null, arg, false);
15787         }
15788     },
15789
15790     // private
15791     loadResponse : function(o, success, response){
15792         delete this.activeRequest;
15793         if(!success){
15794             this.fireEvent("loadexception", this, o, response);
15795             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15796             return;
15797         }
15798         var result;
15799         try {
15800             result = o.reader.read(response);
15801         }catch(e){
15802             this.fireEvent("loadexception", this, o, response, e);
15803             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15804             return;
15805         }
15806         
15807         this.fireEvent("load", this, o, o.request.arg);
15808         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15809     },
15810
15811     // private
15812     update : function(dataSet){
15813
15814     },
15815
15816     // private
15817     updateResponse : function(dataSet){
15818
15819     }
15820 });/*
15821  * Based on:
15822  * Ext JS Library 1.1.1
15823  * Copyright(c) 2006-2007, Ext JS, LLC.
15824  *
15825  * Originally Released Under LGPL - original licence link has changed is not relivant.
15826  *
15827  * Fork - LGPL
15828  * <script type="text/javascript">
15829  */
15830
15831 /**
15832  * @class Roo.data.ScriptTagProxy
15833  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15834  * other than the originating domain of the running page.<br><br>
15835  * <p>
15836  * <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
15837  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15838  * <p>
15839  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15840  * source code that is used as the source inside a &lt;script> tag.<br><br>
15841  * <p>
15842  * In order for the browser to process the returned data, the server must wrap the data object
15843  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15844  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15845  * depending on whether the callback name was passed:
15846  * <p>
15847  * <pre><code>
15848 boolean scriptTag = false;
15849 String cb = request.getParameter("callback");
15850 if (cb != null) {
15851     scriptTag = true;
15852     response.setContentType("text/javascript");
15853 } else {
15854     response.setContentType("application/x-json");
15855 }
15856 Writer out = response.getWriter();
15857 if (scriptTag) {
15858     out.write(cb + "(");
15859 }
15860 out.print(dataBlock.toJsonString());
15861 if (scriptTag) {
15862     out.write(");");
15863 }
15864 </pre></code>
15865  *
15866  * @constructor
15867  * @param {Object} config A configuration object.
15868  */
15869 Roo.data.ScriptTagProxy = function(config){
15870     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15871     Roo.apply(this, config);
15872     this.head = document.getElementsByTagName("head")[0];
15873 };
15874
15875 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15876
15877 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15878     /**
15879      * @cfg {String} url The URL from which to request the data object.
15880      */
15881     /**
15882      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15883      */
15884     timeout : 30000,
15885     /**
15886      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15887      * the server the name of the callback function set up by the load call to process the returned data object.
15888      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15889      * javascript output which calls this named function passing the data object as its only parameter.
15890      */
15891     callbackParam : "callback",
15892     /**
15893      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15894      * name to the request.
15895      */
15896     nocache : true,
15897
15898     /**
15899      * Load data from the configured URL, read the data object into
15900      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15901      * process that block using the passed callback.
15902      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15903      * for the request to the remote server.
15904      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15905      * object into a block of Roo.data.Records.
15906      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15907      * The function must be passed <ul>
15908      * <li>The Record block object</li>
15909      * <li>The "arg" argument from the load function</li>
15910      * <li>A boolean success indicator</li>
15911      * </ul>
15912      * @param {Object} scope The scope in which to call the callback
15913      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15914      */
15915     load : function(params, reader, callback, scope, arg){
15916         if(this.fireEvent("beforeload", this, params) !== false){
15917
15918             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15919
15920             var url = this.url;
15921             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15922             if(this.nocache){
15923                 url += "&_dc=" + (new Date().getTime());
15924             }
15925             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15926             var trans = {
15927                 id : transId,
15928                 cb : "stcCallback"+transId,
15929                 scriptId : "stcScript"+transId,
15930                 params : params,
15931                 arg : arg,
15932                 url : url,
15933                 callback : callback,
15934                 scope : scope,
15935                 reader : reader
15936             };
15937             var conn = this;
15938
15939             window[trans.cb] = function(o){
15940                 conn.handleResponse(o, trans);
15941             };
15942
15943             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15944
15945             if(this.autoAbort !== false){
15946                 this.abort();
15947             }
15948
15949             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15950
15951             var script = document.createElement("script");
15952             script.setAttribute("src", url);
15953             script.setAttribute("type", "text/javascript");
15954             script.setAttribute("id", trans.scriptId);
15955             this.head.appendChild(script);
15956
15957             this.trans = trans;
15958         }else{
15959             callback.call(scope||this, null, arg, false);
15960         }
15961     },
15962
15963     // private
15964     isLoading : function(){
15965         return this.trans ? true : false;
15966     },
15967
15968     /**
15969      * Abort the current server request.
15970      */
15971     abort : function(){
15972         if(this.isLoading()){
15973             this.destroyTrans(this.trans);
15974         }
15975     },
15976
15977     // private
15978     destroyTrans : function(trans, isLoaded){
15979         this.head.removeChild(document.getElementById(trans.scriptId));
15980         clearTimeout(trans.timeoutId);
15981         if(isLoaded){
15982             window[trans.cb] = undefined;
15983             try{
15984                 delete window[trans.cb];
15985             }catch(e){}
15986         }else{
15987             // if hasn't been loaded, wait for load to remove it to prevent script error
15988             window[trans.cb] = function(){
15989                 window[trans.cb] = undefined;
15990                 try{
15991                     delete window[trans.cb];
15992                 }catch(e){}
15993             };
15994         }
15995     },
15996
15997     // private
15998     handleResponse : function(o, trans){
15999         this.trans = false;
16000         this.destroyTrans(trans, true);
16001         var result;
16002         try {
16003             result = trans.reader.readRecords(o);
16004         }catch(e){
16005             this.fireEvent("loadexception", this, o, trans.arg, e);
16006             trans.callback.call(trans.scope||window, null, trans.arg, false);
16007             return;
16008         }
16009         this.fireEvent("load", this, o, trans.arg);
16010         trans.callback.call(trans.scope||window, result, trans.arg, true);
16011     },
16012
16013     // private
16014     handleFailure : function(trans){
16015         this.trans = false;
16016         this.destroyTrans(trans, false);
16017         this.fireEvent("loadexception", this, null, trans.arg);
16018         trans.callback.call(trans.scope||window, null, trans.arg, false);
16019     }
16020 });/*
16021  * Based on:
16022  * Ext JS Library 1.1.1
16023  * Copyright(c) 2006-2007, Ext JS, LLC.
16024  *
16025  * Originally Released Under LGPL - original licence link has changed is not relivant.
16026  *
16027  * Fork - LGPL
16028  * <script type="text/javascript">
16029  */
16030
16031 /**
16032  * @class Roo.data.JsonReader
16033  * @extends Roo.data.DataReader
16034  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16035  * based on mappings in a provided Roo.data.Record constructor.
16036  * 
16037  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16038  * in the reply previously. 
16039  * 
16040  * <p>
16041  * Example code:
16042  * <pre><code>
16043 var RecordDef = Roo.data.Record.create([
16044     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16045     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16046 ]);
16047 var myReader = new Roo.data.JsonReader({
16048     totalProperty: "results",    // The property which contains the total dataset size (optional)
16049     root: "rows",                // The property which contains an Array of row objects
16050     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16051 }, RecordDef);
16052 </code></pre>
16053  * <p>
16054  * This would consume a JSON file like this:
16055  * <pre><code>
16056 { 'results': 2, 'rows': [
16057     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16058     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16059 }
16060 </code></pre>
16061  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16062  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16063  * paged from the remote server.
16064  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16065  * @cfg {String} root name of the property which contains the Array of row objects.
16066  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16067  * @cfg {Array} fields Array of field definition objects
16068  * @constructor
16069  * Create a new JsonReader
16070  * @param {Object} meta Metadata configuration options
16071  * @param {Object} recordType Either an Array of field definition objects,
16072  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16073  */
16074 Roo.data.JsonReader = function(meta, recordType){
16075     
16076     meta = meta || {};
16077     // set some defaults:
16078     Roo.applyIf(meta, {
16079         totalProperty: 'total',
16080         successProperty : 'success',
16081         root : 'data',
16082         id : 'id'
16083     });
16084     
16085     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16086 };
16087 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16088     
16089     readerType : 'Json',
16090     
16091     /**
16092      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16093      * Used by Store query builder to append _requestMeta to params.
16094      * 
16095      */
16096     metaFromRemote : false,
16097     /**
16098      * This method is only used by a DataProxy which has retrieved data from a remote server.
16099      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16100      * @return {Object} data A data block which is used by an Roo.data.Store object as
16101      * a cache of Roo.data.Records.
16102      */
16103     read : function(response){
16104         var json = response.responseText;
16105        
16106         var o = /* eval:var:o */ eval("("+json+")");
16107         if(!o) {
16108             throw {message: "JsonReader.read: Json object not found"};
16109         }
16110         
16111         if(o.metaData){
16112             
16113             delete this.ef;
16114             this.metaFromRemote = true;
16115             this.meta = o.metaData;
16116             this.recordType = Roo.data.Record.create(o.metaData.fields);
16117             this.onMetaChange(this.meta, this.recordType, o);
16118         }
16119         return this.readRecords(o);
16120     },
16121
16122     // private function a store will implement
16123     onMetaChange : function(meta, recordType, o){
16124
16125     },
16126
16127     /**
16128          * @ignore
16129          */
16130     simpleAccess: function(obj, subsc) {
16131         return obj[subsc];
16132     },
16133
16134         /**
16135          * @ignore
16136          */
16137     getJsonAccessor: function(){
16138         var re = /[\[\.]/;
16139         return function(expr) {
16140             try {
16141                 return(re.test(expr))
16142                     ? new Function("obj", "return obj." + expr)
16143                     : function(obj){
16144                         return obj[expr];
16145                     };
16146             } catch(e){}
16147             return Roo.emptyFn;
16148         };
16149     }(),
16150
16151     /**
16152      * Create a data block containing Roo.data.Records from an XML document.
16153      * @param {Object} o An object which contains an Array of row objects in the property specified
16154      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16155      * which contains the total size of the dataset.
16156      * @return {Object} data A data block which is used by an Roo.data.Store object as
16157      * a cache of Roo.data.Records.
16158      */
16159     readRecords : function(o){
16160         /**
16161          * After any data loads, the raw JSON data is available for further custom processing.
16162          * @type Object
16163          */
16164         this.o = o;
16165         var s = this.meta, Record = this.recordType,
16166             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16167
16168 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16169         if (!this.ef) {
16170             if(s.totalProperty) {
16171                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16172                 }
16173                 if(s.successProperty) {
16174                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16175                 }
16176                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16177                 if (s.id) {
16178                         var g = this.getJsonAccessor(s.id);
16179                         this.getId = function(rec) {
16180                                 var r = g(rec);  
16181                                 return (r === undefined || r === "") ? null : r;
16182                         };
16183                 } else {
16184                         this.getId = function(){return null;};
16185                 }
16186             this.ef = [];
16187             for(var jj = 0; jj < fl; jj++){
16188                 f = fi[jj];
16189                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16190                 this.ef[jj] = this.getJsonAccessor(map);
16191             }
16192         }
16193
16194         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16195         if(s.totalProperty){
16196             var vt = parseInt(this.getTotal(o), 10);
16197             if(!isNaN(vt)){
16198                 totalRecords = vt;
16199             }
16200         }
16201         if(s.successProperty){
16202             var vs = this.getSuccess(o);
16203             if(vs === false || vs === 'false'){
16204                 success = false;
16205             }
16206         }
16207         var records = [];
16208         for(var i = 0; i < c; i++){
16209                 var n = root[i];
16210             var values = {};
16211             var id = this.getId(n);
16212             for(var j = 0; j < fl; j++){
16213                 f = fi[j];
16214             var v = this.ef[j](n);
16215             if (!f.convert) {
16216                 Roo.log('missing convert for ' + f.name);
16217                 Roo.log(f);
16218                 continue;
16219             }
16220             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16221             }
16222             var record = new Record(values, id);
16223             record.json = n;
16224             records[i] = record;
16225         }
16226         return {
16227             raw : o,
16228             success : success,
16229             records : records,
16230             totalRecords : totalRecords
16231         };
16232     },
16233     // used when loading children.. @see loadDataFromChildren
16234     toLoadData: function(rec)
16235     {
16236         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16237         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16238         return { data : data, total : data.length };
16239         
16240     }
16241 });/*
16242  * Based on:
16243  * Ext JS Library 1.1.1
16244  * Copyright(c) 2006-2007, Ext JS, LLC.
16245  *
16246  * Originally Released Under LGPL - original licence link has changed is not relivant.
16247  *
16248  * Fork - LGPL
16249  * <script type="text/javascript">
16250  */
16251
16252 /**
16253  * @class Roo.data.ArrayReader
16254  * @extends Roo.data.DataReader
16255  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16256  * Each element of that Array represents a row of data fields. The
16257  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16258  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16259  * <p>
16260  * Example code:.
16261  * <pre><code>
16262 var RecordDef = Roo.data.Record.create([
16263     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16264     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16265 ]);
16266 var myReader = new Roo.data.ArrayReader({
16267     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16268 }, RecordDef);
16269 </code></pre>
16270  * <p>
16271  * This would consume an Array like this:
16272  * <pre><code>
16273 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16274   </code></pre>
16275  
16276  * @constructor
16277  * Create a new JsonReader
16278  * @param {Object} meta Metadata configuration options.
16279  * @param {Object|Array} recordType Either an Array of field definition objects
16280  * 
16281  * @cfg {Array} fields Array of field definition objects
16282  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16283  * as specified to {@link Roo.data.Record#create},
16284  * or an {@link Roo.data.Record} object
16285  *
16286  * 
16287  * created using {@link Roo.data.Record#create}.
16288  */
16289 Roo.data.ArrayReader = function(meta, recordType)
16290 {    
16291     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16292 };
16293
16294 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16295     
16296       /**
16297      * Create a data block containing Roo.data.Records from an XML document.
16298      * @param {Object} o An Array of row objects which represents the dataset.
16299      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16300      * a cache of Roo.data.Records.
16301      */
16302     readRecords : function(o)
16303     {
16304         var sid = this.meta ? this.meta.id : null;
16305         var recordType = this.recordType, fields = recordType.prototype.fields;
16306         var records = [];
16307         var root = o;
16308         for(var i = 0; i < root.length; i++){
16309             var n = root[i];
16310             var values = {};
16311             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16312             for(var j = 0, jlen = fields.length; j < jlen; j++){
16313                 var f = fields.items[j];
16314                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16315                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16316                 v = f.convert(v);
16317                 values[f.name] = v;
16318             }
16319             var record = new recordType(values, id);
16320             record.json = n;
16321             records[records.length] = record;
16322         }
16323         return {
16324             records : records,
16325             totalRecords : records.length
16326         };
16327     },
16328     // used when loading children.. @see loadDataFromChildren
16329     toLoadData: function(rec)
16330     {
16331         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16332         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16333         
16334     }
16335     
16336     
16337 });/*
16338  * - LGPL
16339  * * 
16340  */
16341
16342 /**
16343  * @class Roo.bootstrap.ComboBox
16344  * @extends Roo.bootstrap.TriggerField
16345  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16346  * @cfg {Boolean} append (true|false) default false
16347  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16348  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16349  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16350  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16351  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16352  * @cfg {Boolean} animate default true
16353  * @cfg {Boolean} emptyResultText only for touch device
16354  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16355  * @cfg {String} emptyTitle default ''
16356  * @cfg {Number} width fixed with? experimental
16357  * @constructor
16358  * Create a new ComboBox.
16359  * @param {Object} config Configuration options
16360  */
16361 Roo.bootstrap.ComboBox = function(config){
16362     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16363     this.addEvents({
16364         /**
16365          * @event expand
16366          * Fires when the dropdown list is expanded
16367         * @param {Roo.bootstrap.ComboBox} combo This combo box
16368         */
16369         'expand' : true,
16370         /**
16371          * @event collapse
16372          * Fires when the dropdown list is collapsed
16373         * @param {Roo.bootstrap.ComboBox} combo This combo box
16374         */
16375         'collapse' : true,
16376         /**
16377          * @event beforeselect
16378          * Fires before a list item is selected. Return false to cancel the selection.
16379         * @param {Roo.bootstrap.ComboBox} combo This combo box
16380         * @param {Roo.data.Record} record The data record returned from the underlying store
16381         * @param {Number} index The index of the selected item in the dropdown list
16382         */
16383         'beforeselect' : true,
16384         /**
16385          * @event select
16386          * Fires when a list item is selected
16387         * @param {Roo.bootstrap.ComboBox} combo This combo box
16388         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16389         * @param {Number} index The index of the selected item in the dropdown list
16390         */
16391         'select' : true,
16392         /**
16393          * @event beforequery
16394          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16395          * The event object passed has these properties:
16396         * @param {Roo.bootstrap.ComboBox} combo This combo box
16397         * @param {String} query The query
16398         * @param {Boolean} forceAll true to force "all" query
16399         * @param {Boolean} cancel true to cancel the query
16400         * @param {Object} e The query event object
16401         */
16402         'beforequery': true,
16403          /**
16404          * @event add
16405          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16406         * @param {Roo.bootstrap.ComboBox} combo This combo box
16407         */
16408         'add' : true,
16409         /**
16410          * @event edit
16411          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16412         * @param {Roo.bootstrap.ComboBox} combo This combo box
16413         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16414         */
16415         'edit' : true,
16416         /**
16417          * @event remove
16418          * Fires when the remove value from the combobox array
16419         * @param {Roo.bootstrap.ComboBox} combo This combo box
16420         */
16421         'remove' : true,
16422         /**
16423          * @event afterremove
16424          * Fires when the remove value from the combobox array
16425         * @param {Roo.bootstrap.ComboBox} combo This combo box
16426         */
16427         'afterremove' : true,
16428         /**
16429          * @event specialfilter
16430          * Fires when specialfilter
16431             * @param {Roo.bootstrap.ComboBox} combo This combo box
16432             */
16433         'specialfilter' : true,
16434         /**
16435          * @event tick
16436          * Fires when tick the element
16437             * @param {Roo.bootstrap.ComboBox} combo This combo box
16438             */
16439         'tick' : true,
16440         /**
16441          * @event touchviewdisplay
16442          * Fires when touch view require special display (default is using displayField)
16443             * @param {Roo.bootstrap.ComboBox} combo This combo box
16444             * @param {Object} cfg set html .
16445             */
16446         'touchviewdisplay' : true
16447         
16448     });
16449     
16450     this.item = [];
16451     this.tickItems = [];
16452     
16453     this.selectedIndex = -1;
16454     if(this.mode == 'local'){
16455         if(config.queryDelay === undefined){
16456             this.queryDelay = 10;
16457         }
16458         if(config.minChars === undefined){
16459             this.minChars = 0;
16460         }
16461     }
16462 };
16463
16464 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16465      
16466     /**
16467      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16468      * rendering into an Roo.Editor, defaults to false)
16469      */
16470     /**
16471      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16472      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16473      */
16474     /**
16475      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16476      */
16477     /**
16478      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16479      * the dropdown list (defaults to undefined, with no header element)
16480      */
16481
16482      /**
16483      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16484      */
16485      
16486      /**
16487      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16488      */
16489     listWidth: undefined,
16490     /**
16491      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16492      * mode = 'remote' or 'text' if mode = 'local')
16493      */
16494     displayField: undefined,
16495     
16496     /**
16497      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16498      * mode = 'remote' or 'value' if mode = 'local'). 
16499      * Note: use of a valueField requires the user make a selection
16500      * in order for a value to be mapped.
16501      */
16502     valueField: undefined,
16503     /**
16504      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16505      */
16506     modalTitle : '',
16507     
16508     /**
16509      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16510      * field's data value (defaults to the underlying DOM element's name)
16511      */
16512     hiddenName: undefined,
16513     /**
16514      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16515      */
16516     listClass: '',
16517     /**
16518      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16519      */
16520     selectedClass: 'active',
16521     
16522     /**
16523      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16524      */
16525     shadow:'sides',
16526     /**
16527      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16528      * anchor positions (defaults to 'tl-bl')
16529      */
16530     listAlign: 'tl-bl?',
16531     /**
16532      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16533      */
16534     maxHeight: 300,
16535     /**
16536      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16537      * query specified by the allQuery config option (defaults to 'query')
16538      */
16539     triggerAction: 'query',
16540     /**
16541      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16542      * (defaults to 4, does not apply if editable = false)
16543      */
16544     minChars : 4,
16545     /**
16546      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16547      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16548      */
16549     typeAhead: false,
16550     /**
16551      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16552      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16553      */
16554     queryDelay: 500,
16555     /**
16556      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16557      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16558      */
16559     pageSize: 0,
16560     /**
16561      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16562      * when editable = true (defaults to false)
16563      */
16564     selectOnFocus:false,
16565     /**
16566      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16567      */
16568     queryParam: 'query',
16569     /**
16570      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16571      * when mode = 'remote' (defaults to 'Loading...')
16572      */
16573     loadingText: 'Loading...',
16574     /**
16575      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16576      */
16577     resizable: false,
16578     /**
16579      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16580      */
16581     handleHeight : 8,
16582     /**
16583      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16584      * traditional select (defaults to true)
16585      */
16586     editable: true,
16587     /**
16588      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16589      */
16590     allQuery: '',
16591     /**
16592      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16593      */
16594     mode: 'remote',
16595     /**
16596      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16597      * listWidth has a higher value)
16598      */
16599     minListWidth : 70,
16600     /**
16601      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16602      * allow the user to set arbitrary text into the field (defaults to false)
16603      */
16604     forceSelection:false,
16605     /**
16606      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16607      * if typeAhead = true (defaults to 250)
16608      */
16609     typeAheadDelay : 250,
16610     /**
16611      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16612      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16613      */
16614     valueNotFoundText : undefined,
16615     /**
16616      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16617      */
16618     blockFocus : false,
16619     
16620     /**
16621      * @cfg {Boolean} disableClear Disable showing of clear button.
16622      */
16623     disableClear : false,
16624     /**
16625      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16626      */
16627     alwaysQuery : false,
16628     
16629     /**
16630      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16631      */
16632     multiple : false,
16633     
16634     /**
16635      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16636      */
16637     invalidClass : "has-warning",
16638     
16639     /**
16640      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16641      */
16642     validClass : "has-success",
16643     
16644     /**
16645      * @cfg {Boolean} specialFilter (true|false) special filter default false
16646      */
16647     specialFilter : false,
16648     
16649     /**
16650      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16651      */
16652     mobileTouchView : true,
16653     
16654     /**
16655      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16656      */
16657     useNativeIOS : false,
16658     
16659     /**
16660      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16661      */
16662     mobile_restrict_height : false,
16663     
16664     ios_options : false,
16665     
16666     //private
16667     addicon : false,
16668     editicon: false,
16669     
16670     page: 0,
16671     hasQuery: false,
16672     append: false,
16673     loadNext: false,
16674     autoFocus : true,
16675     tickable : false,
16676     btnPosition : 'right',
16677     triggerList : true,
16678     showToggleBtn : true,
16679     animate : true,
16680     emptyResultText: 'Empty',
16681     triggerText : 'Select',
16682     emptyTitle : '',
16683     width : false,
16684     
16685     // element that contains real text value.. (when hidden is used..)
16686     
16687     getAutoCreate : function()
16688     {   
16689         var cfg = false;
16690         //render
16691         /*
16692          * Render classic select for iso
16693          */
16694         
16695         if(Roo.isIOS && this.useNativeIOS){
16696             cfg = this.getAutoCreateNativeIOS();
16697             return cfg;
16698         }
16699         
16700         /*
16701          * Touch Devices
16702          */
16703         
16704         if(Roo.isTouch && this.mobileTouchView){
16705             cfg = this.getAutoCreateTouchView();
16706             return cfg;;
16707         }
16708         
16709         /*
16710          *  Normal ComboBox
16711          */
16712         if(!this.tickable){
16713             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16714             return cfg;
16715         }
16716         
16717         /*
16718          *  ComboBox with tickable selections
16719          */
16720              
16721         var align = this.labelAlign || this.parentLabelAlign();
16722         
16723         cfg = {
16724             cls : 'form-group roo-combobox-tickable' //input-group
16725         };
16726         
16727         var btn_text_select = '';
16728         var btn_text_done = '';
16729         var btn_text_cancel = '';
16730         
16731         if (this.btn_text_show) {
16732             btn_text_select = 'Select';
16733             btn_text_done = 'Done';
16734             btn_text_cancel = 'Cancel'; 
16735         }
16736         
16737         var buttons = {
16738             tag : 'div',
16739             cls : 'tickable-buttons',
16740             cn : [
16741                 {
16742                     tag : 'button',
16743                     type : 'button',
16744                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16745                     //html : this.triggerText
16746                     html: btn_text_select
16747                 },
16748                 {
16749                     tag : 'button',
16750                     type : 'button',
16751                     name : 'ok',
16752                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16753                     //html : 'Done'
16754                     html: btn_text_done
16755                 },
16756                 {
16757                     tag : 'button',
16758                     type : 'button',
16759                     name : 'cancel',
16760                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16761                     //html : 'Cancel'
16762                     html: btn_text_cancel
16763                 }
16764             ]
16765         };
16766         
16767         if(this.editable){
16768             buttons.cn.unshift({
16769                 tag: 'input',
16770                 cls: 'roo-select2-search-field-input'
16771             });
16772         }
16773         
16774         var _this = this;
16775         
16776         Roo.each(buttons.cn, function(c){
16777             if (_this.size) {
16778                 c.cls += ' btn-' + _this.size;
16779             }
16780
16781             if (_this.disabled) {
16782                 c.disabled = true;
16783             }
16784         });
16785         
16786         var box = {
16787             tag: 'div',
16788             style : 'display: contents',
16789             cn: [
16790                 {
16791                     tag: 'input',
16792                     type : 'hidden',
16793                     cls: 'form-hidden-field'
16794                 },
16795                 {
16796                     tag: 'ul',
16797                     cls: 'roo-select2-choices',
16798                     cn:[
16799                         {
16800                             tag: 'li',
16801                             cls: 'roo-select2-search-field',
16802                             cn: [
16803                                 buttons
16804                             ]
16805                         }
16806                     ]
16807                 }
16808             ]
16809         };
16810         
16811         var combobox = {
16812             cls: 'roo-select2-container input-group roo-select2-container-multi',
16813             cn: [
16814                 
16815                 box
16816 //                {
16817 //                    tag: 'ul',
16818 //                    cls: 'typeahead typeahead-long dropdown-menu',
16819 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16820 //                }
16821             ]
16822         };
16823         
16824         if(this.hasFeedback && !this.allowBlank){
16825             
16826             var feedback = {
16827                 tag: 'span',
16828                 cls: 'glyphicon form-control-feedback'
16829             };
16830
16831             combobox.cn.push(feedback);
16832         }
16833         
16834         
16835         
16836         var indicator = {
16837             tag : 'i',
16838             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16839             tooltip : 'This field is required'
16840         };
16841         if (Roo.bootstrap.version == 4) {
16842             indicator = {
16843                 tag : 'i',
16844                 style : 'display:none'
16845             };
16846         }
16847         if (align ==='left' && this.fieldLabel.length) {
16848             
16849             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16850             
16851             cfg.cn = [
16852                 indicator,
16853                 {
16854                     tag: 'label',
16855                     'for' :  id,
16856                     cls : 'control-label col-form-label',
16857                     html : this.fieldLabel
16858
16859                 },
16860                 {
16861                     cls : "", 
16862                     cn: [
16863                         combobox
16864                     ]
16865                 }
16866
16867             ];
16868             
16869             var labelCfg = cfg.cn[1];
16870             var contentCfg = cfg.cn[2];
16871             
16872
16873             if(this.indicatorpos == 'right'){
16874                 
16875                 cfg.cn = [
16876                     {
16877                         tag: 'label',
16878                         'for' :  id,
16879                         cls : 'control-label col-form-label',
16880                         cn : [
16881                             {
16882                                 tag : 'span',
16883                                 html : this.fieldLabel
16884                             },
16885                             indicator
16886                         ]
16887                     },
16888                     {
16889                         cls : "",
16890                         cn: [
16891                             combobox
16892                         ]
16893                     }
16894
16895                 ];
16896                 
16897                 
16898                 
16899                 labelCfg = cfg.cn[0];
16900                 contentCfg = cfg.cn[1];
16901             
16902             }
16903             
16904             if(this.labelWidth > 12){
16905                 labelCfg.style = "width: " + this.labelWidth + 'px';
16906             }
16907             if(this.width * 1 > 0){
16908                 contentCfg.style = "width: " + this.width + 'px';
16909             }
16910             if(this.labelWidth < 13 && this.labelmd == 0){
16911                 this.labelmd = this.labelWidth;
16912             }
16913             
16914             if(this.labellg > 0){
16915                 labelCfg.cls += ' col-lg-' + this.labellg;
16916                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16917             }
16918             
16919             if(this.labelmd > 0){
16920                 labelCfg.cls += ' col-md-' + this.labelmd;
16921                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16922             }
16923             
16924             if(this.labelsm > 0){
16925                 labelCfg.cls += ' col-sm-' + this.labelsm;
16926                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16927             }
16928             
16929             if(this.labelxs > 0){
16930                 labelCfg.cls += ' col-xs-' + this.labelxs;
16931                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16932             }
16933                 
16934                 
16935         } else if ( this.fieldLabel.length) {
16936 //                Roo.log(" label");
16937                  cfg.cn = [
16938                    indicator,
16939                     {
16940                         tag: 'label',
16941                         //cls : 'input-group-addon',
16942                         html : this.fieldLabel
16943                     },
16944                     combobox
16945                 ];
16946                 
16947                 if(this.indicatorpos == 'right'){
16948                     cfg.cn = [
16949                         {
16950                             tag: 'label',
16951                             //cls : 'input-group-addon',
16952                             html : this.fieldLabel
16953                         },
16954                         indicator,
16955                         combobox
16956                     ];
16957                     
16958                 }
16959
16960         } else {
16961             
16962 //                Roo.log(" no label && no align");
16963                 cfg = combobox
16964                      
16965                 
16966         }
16967          
16968         var settings=this;
16969         ['xs','sm','md','lg'].map(function(size){
16970             if (settings[size]) {
16971                 cfg.cls += ' col-' + size + '-' + settings[size];
16972             }
16973         });
16974         
16975         return cfg;
16976         
16977     },
16978     
16979     _initEventsCalled : false,
16980     
16981     // private
16982     initEvents: function()
16983     {   
16984         if (this._initEventsCalled) { // as we call render... prevent looping...
16985             return;
16986         }
16987         this._initEventsCalled = true;
16988         
16989         if (!this.store) {
16990             throw "can not find store for combo";
16991         }
16992         
16993         this.indicator = this.indicatorEl();
16994         
16995         this.store = Roo.factory(this.store, Roo.data);
16996         this.store.parent = this;
16997         
16998         // if we are building from html. then this element is so complex, that we can not really
16999         // use the rendered HTML.
17000         // so we have to trash and replace the previous code.
17001         if (Roo.XComponent.build_from_html) {
17002             // remove this element....
17003             var e = this.el.dom, k=0;
17004             while (e ) { e = e.previousSibling;  ++k;}
17005
17006             this.el.remove();
17007             
17008             this.el=false;
17009             this.rendered = false;
17010             
17011             this.render(this.parent().getChildContainer(true), k);
17012         }
17013         
17014         if(Roo.isIOS && this.useNativeIOS){
17015             this.initIOSView();
17016             return;
17017         }
17018         
17019         /*
17020          * Touch Devices
17021          */
17022         
17023         if(Roo.isTouch && this.mobileTouchView){
17024             this.initTouchView();
17025             return;
17026         }
17027         
17028         if(this.tickable){
17029             this.initTickableEvents();
17030             return;
17031         }
17032         
17033         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17034         
17035         if(this.hiddenName){
17036             
17037             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17038             
17039             this.hiddenField.dom.value =
17040                 this.hiddenValue !== undefined ? this.hiddenValue :
17041                 this.value !== undefined ? this.value : '';
17042
17043             // prevent input submission
17044             this.el.dom.removeAttribute('name');
17045             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17046              
17047              
17048         }
17049         //if(Roo.isGecko){
17050         //    this.el.dom.setAttribute('autocomplete', 'off');
17051         //}
17052         
17053         var cls = 'x-combo-list';
17054         
17055         //this.list = new Roo.Layer({
17056         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17057         //});
17058         
17059         var _this = this;
17060         
17061         (function(){
17062             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17063             _this.list.setWidth(lw);
17064         }).defer(100);
17065         
17066         this.list.on('mouseover', this.onViewOver, this);
17067         this.list.on('mousemove', this.onViewMove, this);
17068         this.list.on('scroll', this.onViewScroll, this);
17069         
17070         /*
17071         this.list.swallowEvent('mousewheel');
17072         this.assetHeight = 0;
17073
17074         if(this.title){
17075             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17076             this.assetHeight += this.header.getHeight();
17077         }
17078
17079         this.innerList = this.list.createChild({cls:cls+'-inner'});
17080         this.innerList.on('mouseover', this.onViewOver, this);
17081         this.innerList.on('mousemove', this.onViewMove, this);
17082         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17083         
17084         if(this.allowBlank && !this.pageSize && !this.disableClear){
17085             this.footer = this.list.createChild({cls:cls+'-ft'});
17086             this.pageTb = new Roo.Toolbar(this.footer);
17087            
17088         }
17089         if(this.pageSize){
17090             this.footer = this.list.createChild({cls:cls+'-ft'});
17091             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17092                     {pageSize: this.pageSize});
17093             
17094         }
17095         
17096         if (this.pageTb && this.allowBlank && !this.disableClear) {
17097             var _this = this;
17098             this.pageTb.add(new Roo.Toolbar.Fill(), {
17099                 cls: 'x-btn-icon x-btn-clear',
17100                 text: '&#160;',
17101                 handler: function()
17102                 {
17103                     _this.collapse();
17104                     _this.clearValue();
17105                     _this.onSelect(false, -1);
17106                 }
17107             });
17108         }
17109         if (this.footer) {
17110             this.assetHeight += this.footer.getHeight();
17111         }
17112         */
17113             
17114         if(!this.tpl){
17115             this.tpl = Roo.bootstrap.version == 4 ?
17116                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17117                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17118         }
17119
17120         this.view = new Roo.View(this.list, this.tpl, {
17121             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17122         });
17123         //this.view.wrapEl.setDisplayed(false);
17124         this.view.on('click', this.onViewClick, this);
17125         
17126         
17127         this.store.on('beforeload', this.onBeforeLoad, this);
17128         this.store.on('load', this.onLoad, this);
17129         this.store.on('loadexception', this.onLoadException, this);
17130         /*
17131         if(this.resizable){
17132             this.resizer = new Roo.Resizable(this.list,  {
17133                pinned:true, handles:'se'
17134             });
17135             this.resizer.on('resize', function(r, w, h){
17136                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17137                 this.listWidth = w;
17138                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17139                 this.restrictHeight();
17140             }, this);
17141             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17142         }
17143         */
17144         if(!this.editable){
17145             this.editable = true;
17146             this.setEditable(false);
17147         }
17148         
17149         /*
17150         
17151         if (typeof(this.events.add.listeners) != 'undefined') {
17152             
17153             this.addicon = this.wrap.createChild(
17154                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17155        
17156             this.addicon.on('click', function(e) {
17157                 this.fireEvent('add', this);
17158             }, this);
17159         }
17160         if (typeof(this.events.edit.listeners) != 'undefined') {
17161             
17162             this.editicon = this.wrap.createChild(
17163                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17164             if (this.addicon) {
17165                 this.editicon.setStyle('margin-left', '40px');
17166             }
17167             this.editicon.on('click', function(e) {
17168                 
17169                 // we fire even  if inothing is selected..
17170                 this.fireEvent('edit', this, this.lastData );
17171                 
17172             }, this);
17173         }
17174         */
17175         
17176         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17177             "up" : function(e){
17178                 this.inKeyMode = true;
17179                 this.selectPrev();
17180             },
17181
17182             "down" : function(e){
17183                 if(!this.isExpanded()){
17184                     this.onTriggerClick();
17185                 }else{
17186                     this.inKeyMode = true;
17187                     this.selectNext();
17188                 }
17189             },
17190
17191             "enter" : function(e){
17192 //                this.onViewClick();
17193                 //return true;
17194                 this.collapse();
17195                 
17196                 if(this.fireEvent("specialkey", this, e)){
17197                     this.onViewClick(false);
17198                 }
17199                 
17200                 return true;
17201             },
17202
17203             "esc" : function(e){
17204                 this.collapse();
17205             },
17206
17207             "tab" : function(e){
17208                 this.collapse();
17209                 
17210                 if(this.fireEvent("specialkey", this, e)){
17211                     this.onViewClick(false);
17212                 }
17213                 
17214                 return true;
17215             },
17216
17217             scope : this,
17218
17219             doRelay : function(foo, bar, hname){
17220                 if(hname == 'down' || this.scope.isExpanded()){
17221                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17222                 }
17223                 return true;
17224             },
17225
17226             forceKeyDown: true
17227         });
17228         
17229         
17230         this.queryDelay = Math.max(this.queryDelay || 10,
17231                 this.mode == 'local' ? 10 : 250);
17232         
17233         
17234         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17235         
17236         if(this.typeAhead){
17237             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17238         }
17239         if(this.editable !== false){
17240             this.inputEl().on("keyup", this.onKeyUp, this);
17241         }
17242         if(this.forceSelection){
17243             this.inputEl().on('blur', this.doForce, this);
17244         }
17245         
17246         if(this.multiple){
17247             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17248             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17249         }
17250     },
17251     
17252     initTickableEvents: function()
17253     {   
17254         this.createList();
17255         
17256         if(this.hiddenName){
17257             
17258             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17259             
17260             this.hiddenField.dom.value =
17261                 this.hiddenValue !== undefined ? this.hiddenValue :
17262                 this.value !== undefined ? this.value : '';
17263
17264             // prevent input submission
17265             this.el.dom.removeAttribute('name');
17266             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17267              
17268              
17269         }
17270         
17271 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17272         
17273         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17274         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17275         if(this.triggerList){
17276             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17277         }
17278          
17279         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17280         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17281         
17282         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17283         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17284         
17285         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17286         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17287         
17288         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17289         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17290         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17291         
17292         this.okBtn.hide();
17293         this.cancelBtn.hide();
17294         
17295         var _this = this;
17296         
17297         (function(){
17298             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17299             _this.list.setWidth(lw);
17300         }).defer(100);
17301         
17302         this.list.on('mouseover', this.onViewOver, this);
17303         this.list.on('mousemove', this.onViewMove, this);
17304         
17305         this.list.on('scroll', this.onViewScroll, this);
17306         
17307         if(!this.tpl){
17308             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17309                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17310         }
17311
17312         this.view = new Roo.View(this.list, this.tpl, {
17313             singleSelect:true,
17314             tickable:true,
17315             parent:this,
17316             store: this.store,
17317             selectedClass: this.selectedClass
17318         });
17319         
17320         //this.view.wrapEl.setDisplayed(false);
17321         this.view.on('click', this.onViewClick, this);
17322         
17323         
17324         
17325         this.store.on('beforeload', this.onBeforeLoad, this);
17326         this.store.on('load', this.onLoad, this);
17327         this.store.on('loadexception', this.onLoadException, this);
17328         
17329         if(this.editable){
17330             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17331                 "up" : function(e){
17332                     this.inKeyMode = true;
17333                     this.selectPrev();
17334                 },
17335
17336                 "down" : function(e){
17337                     this.inKeyMode = true;
17338                     this.selectNext();
17339                 },
17340
17341                 "enter" : function(e){
17342                     if(this.fireEvent("specialkey", this, e)){
17343                         this.onViewClick(false);
17344                     }
17345                     
17346                     return true;
17347                 },
17348
17349                 "esc" : function(e){
17350                     this.onTickableFooterButtonClick(e, false, false);
17351                 },
17352
17353                 "tab" : function(e){
17354                     this.fireEvent("specialkey", this, e);
17355                     
17356                     this.onTickableFooterButtonClick(e, false, false);
17357                     
17358                     return true;
17359                 },
17360
17361                 scope : this,
17362
17363                 doRelay : function(e, fn, key){
17364                     if(this.scope.isExpanded()){
17365                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17366                     }
17367                     return true;
17368                 },
17369
17370                 forceKeyDown: true
17371             });
17372         }
17373         
17374         this.queryDelay = Math.max(this.queryDelay || 10,
17375                 this.mode == 'local' ? 10 : 250);
17376         
17377         
17378         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17379         
17380         if(this.typeAhead){
17381             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17382         }
17383         
17384         if(this.editable !== false){
17385             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17386         }
17387         
17388         this.indicator = this.indicatorEl();
17389         
17390         if(this.indicator){
17391             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17392             this.indicator.hide();
17393         }
17394         
17395     },
17396
17397     onDestroy : function(){
17398         if(this.view){
17399             this.view.setStore(null);
17400             this.view.el.removeAllListeners();
17401             this.view.el.remove();
17402             this.view.purgeListeners();
17403         }
17404         if(this.list){
17405             this.list.dom.innerHTML  = '';
17406         }
17407         
17408         if(this.store){
17409             this.store.un('beforeload', this.onBeforeLoad, this);
17410             this.store.un('load', this.onLoad, this);
17411             this.store.un('loadexception', this.onLoadException, this);
17412         }
17413         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17414     },
17415
17416     // private
17417     fireKey : function(e){
17418         if(e.isNavKeyPress() && !this.list.isVisible()){
17419             this.fireEvent("specialkey", this, e);
17420         }
17421     },
17422
17423     // private
17424     onResize: function(w, h)
17425     {
17426         
17427         
17428 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17429 //        
17430 //        if(typeof w != 'number'){
17431 //            // we do not handle it!?!?
17432 //            return;
17433 //        }
17434 //        var tw = this.trigger.getWidth();
17435 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17436 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17437 //        var x = w - tw;
17438 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17439 //            
17440 //        //this.trigger.setStyle('left', x+'px');
17441 //        
17442 //        if(this.list && this.listWidth === undefined){
17443 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17444 //            this.list.setWidth(lw);
17445 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17446 //        }
17447         
17448     
17449         
17450     },
17451
17452     /**
17453      * Allow or prevent the user from directly editing the field text.  If false is passed,
17454      * the user will only be able to select from the items defined in the dropdown list.  This method
17455      * is the runtime equivalent of setting the 'editable' config option at config time.
17456      * @param {Boolean} value True to allow the user to directly edit the field text
17457      */
17458     setEditable : function(value){
17459         if(value == this.editable){
17460             return;
17461         }
17462         this.editable = value;
17463         if(!value){
17464             this.inputEl().dom.setAttribute('readOnly', true);
17465             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17466             this.inputEl().addClass('x-combo-noedit');
17467         }else{
17468             this.inputEl().dom.removeAttribute('readOnly');
17469             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17470             this.inputEl().removeClass('x-combo-noedit');
17471         }
17472     },
17473
17474     // private
17475     
17476     onBeforeLoad : function(combo,opts){
17477         if(!this.hasFocus){
17478             return;
17479         }
17480          if (!opts.add) {
17481             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17482          }
17483         this.restrictHeight();
17484         this.selectedIndex = -1;
17485     },
17486
17487     // private
17488     onLoad : function(){
17489         
17490         this.hasQuery = false;
17491         
17492         if(!this.hasFocus){
17493             return;
17494         }
17495         
17496         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17497             this.loading.hide();
17498         }
17499         
17500         if(this.store.getCount() > 0){
17501             
17502             this.expand();
17503             this.restrictHeight();
17504             if(this.lastQuery == this.allQuery){
17505                 if(this.editable && !this.tickable){
17506                     this.inputEl().dom.select();
17507                 }
17508                 
17509                 if(
17510                     !this.selectByValue(this.value, true) &&
17511                     this.autoFocus && 
17512                     (
17513                         !this.store.lastOptions ||
17514                         typeof(this.store.lastOptions.add) == 'undefined' || 
17515                         this.store.lastOptions.add != true
17516                     )
17517                 ){
17518                     this.select(0, true);
17519                 }
17520             }else{
17521                 if(this.autoFocus){
17522                     this.selectNext();
17523                 }
17524                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17525                     this.taTask.delay(this.typeAheadDelay);
17526                 }
17527             }
17528         }else{
17529             this.onEmptyResults();
17530         }
17531         
17532         //this.el.focus();
17533     },
17534     // private
17535     onLoadException : function()
17536     {
17537         this.hasQuery = false;
17538         
17539         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17540             this.loading.hide();
17541         }
17542         
17543         if(this.tickable && this.editable){
17544             return;
17545         }
17546         
17547         this.collapse();
17548         // only causes errors at present
17549         //Roo.log(this.store.reader.jsonData);
17550         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17551             // fixme
17552             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17553         //}
17554         
17555         
17556     },
17557     // private
17558     onTypeAhead : function(){
17559         if(this.store.getCount() > 0){
17560             var r = this.store.getAt(0);
17561             var newValue = r.data[this.displayField];
17562             var len = newValue.length;
17563             var selStart = this.getRawValue().length;
17564             
17565             if(selStart != len){
17566                 this.setRawValue(newValue);
17567                 this.selectText(selStart, newValue.length);
17568             }
17569         }
17570     },
17571
17572     // private
17573     onSelect : function(record, index){
17574         
17575         if(this.fireEvent('beforeselect', this, record, index) !== false){
17576         
17577             this.setFromData(index > -1 ? record.data : false);
17578             
17579             this.collapse();
17580             this.fireEvent('select', this, record, index);
17581         }
17582     },
17583
17584     /**
17585      * Returns the currently selected field value or empty string if no value is set.
17586      * @return {String} value The selected value
17587      */
17588     getValue : function()
17589     {
17590         if(Roo.isIOS && this.useNativeIOS){
17591             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17592         }
17593         
17594         if(this.multiple){
17595             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17596         }
17597         
17598         if(this.valueField){
17599             return typeof this.value != 'undefined' ? this.value : '';
17600         }else{
17601             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17602         }
17603     },
17604     
17605     getRawValue : function()
17606     {
17607         if(Roo.isIOS && this.useNativeIOS){
17608             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17609         }
17610         
17611         var v = this.inputEl().getValue();
17612         
17613         return v;
17614     },
17615
17616     /**
17617      * Clears any text/value currently set in the field
17618      */
17619     clearValue : function(){
17620         
17621         if(this.hiddenField){
17622             this.hiddenField.dom.value = '';
17623         }
17624         this.value = '';
17625         this.setRawValue('');
17626         this.lastSelectionText = '';
17627         this.lastData = false;
17628         
17629         var close = this.closeTriggerEl();
17630         
17631         if(close){
17632             close.hide();
17633         }
17634         
17635         this.validate();
17636         
17637     },
17638
17639     /**
17640      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17641      * will be displayed in the field.  If the value does not match the data value of an existing item,
17642      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17643      * Otherwise the field will be blank (although the value will still be set).
17644      * @param {String} value The value to match
17645      */
17646     setValue : function(v)
17647     {
17648         if(Roo.isIOS && this.useNativeIOS){
17649             this.setIOSValue(v);
17650             return;
17651         }
17652         
17653         if(this.multiple){
17654             this.syncValue();
17655             return;
17656         }
17657         
17658         var text = v;
17659         if(this.valueField){
17660             var r = this.findRecord(this.valueField, v);
17661             if(r){
17662                 text = r.data[this.displayField];
17663             }else if(this.valueNotFoundText !== undefined){
17664                 text = this.valueNotFoundText;
17665             }
17666         }
17667         this.lastSelectionText = text;
17668         if(this.hiddenField){
17669             this.hiddenField.dom.value = v;
17670         }
17671         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17672         this.value = v;
17673         
17674         var close = this.closeTriggerEl();
17675         
17676         if(close){
17677             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17678         }
17679         
17680         this.validate();
17681     },
17682     /**
17683      * @property {Object} the last set data for the element
17684      */
17685     
17686     lastData : false,
17687     /**
17688      * Sets the value of the field based on a object which is related to the record format for the store.
17689      * @param {Object} value the value to set as. or false on reset?
17690      */
17691     setFromData : function(o){
17692         
17693         if(this.multiple){
17694             this.addItem(o);
17695             return;
17696         }
17697             
17698         var dv = ''; // display value
17699         var vv = ''; // value value..
17700         this.lastData = o;
17701         if (this.displayField) {
17702             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17703         } else {
17704             // this is an error condition!!!
17705             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17706         }
17707         
17708         if(this.valueField){
17709             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17710         }
17711         
17712         var close = this.closeTriggerEl();
17713         
17714         if(close){
17715             if(dv.length || vv * 1 > 0){
17716                 close.show() ;
17717                 this.blockFocus=true;
17718             } else {
17719                 close.hide();
17720             }             
17721         }
17722         
17723         if(this.hiddenField){
17724             this.hiddenField.dom.value = vv;
17725             
17726             this.lastSelectionText = dv;
17727             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17728             this.value = vv;
17729             return;
17730         }
17731         // no hidden field.. - we store the value in 'value', but still display
17732         // display field!!!!
17733         this.lastSelectionText = dv;
17734         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17735         this.value = vv;
17736         
17737         
17738         
17739     },
17740     // private
17741     reset : function(){
17742         // overridden so that last data is reset..
17743         
17744         if(this.multiple){
17745             this.clearItem();
17746             return;
17747         }
17748         
17749         this.setValue(this.originalValue);
17750         //this.clearInvalid();
17751         this.lastData = false;
17752         if (this.view) {
17753             this.view.clearSelections();
17754         }
17755         
17756         this.validate();
17757     },
17758     // private
17759     findRecord : function(prop, value){
17760         var record;
17761         if(this.store.getCount() > 0){
17762             this.store.each(function(r){
17763                 if(r.data[prop] == value){
17764                     record = r;
17765                     return false;
17766                 }
17767                 return true;
17768             });
17769         }
17770         return record;
17771     },
17772     
17773     getName: function()
17774     {
17775         // returns hidden if it's set..
17776         if (!this.rendered) {return ''};
17777         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17778         
17779     },
17780     // private
17781     onViewMove : function(e, t){
17782         this.inKeyMode = false;
17783     },
17784
17785     // private
17786     onViewOver : function(e, t){
17787         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17788             return;
17789         }
17790         var item = this.view.findItemFromChild(t);
17791         
17792         if(item){
17793             var index = this.view.indexOf(item);
17794             this.select(index, false);
17795         }
17796     },
17797
17798     // private
17799     onViewClick : function(view, doFocus, el, e)
17800     {
17801         var index = this.view.getSelectedIndexes()[0];
17802         
17803         var r = this.store.getAt(index);
17804         
17805         if(this.tickable){
17806             
17807             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17808                 return;
17809             }
17810             
17811             var rm = false;
17812             var _this = this;
17813             
17814             Roo.each(this.tickItems, function(v,k){
17815                 
17816                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17817                     Roo.log(v);
17818                     _this.tickItems.splice(k, 1);
17819                     
17820                     if(typeof(e) == 'undefined' && view == false){
17821                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17822                     }
17823                     
17824                     rm = true;
17825                     return;
17826                 }
17827             });
17828             
17829             if(rm){
17830                 return;
17831             }
17832             
17833             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17834                 this.tickItems.push(r.data);
17835             }
17836             
17837             if(typeof(e) == 'undefined' && view == false){
17838                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17839             }
17840                     
17841             return;
17842         }
17843         
17844         if(r){
17845             this.onSelect(r, index);
17846         }
17847         if(doFocus !== false && !this.blockFocus){
17848             this.inputEl().focus();
17849         }
17850     },
17851
17852     // private
17853     restrictHeight : function(){
17854         //this.innerList.dom.style.height = '';
17855         //var inner = this.innerList.dom;
17856         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17857         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17858         //this.list.beginUpdate();
17859         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17860         this.list.alignTo(this.inputEl(), this.listAlign);
17861         this.list.alignTo(this.inputEl(), this.listAlign);
17862         //this.list.endUpdate();
17863     },
17864
17865     // private
17866     onEmptyResults : function(){
17867         
17868         if(this.tickable && this.editable){
17869             this.hasFocus = false;
17870             this.restrictHeight();
17871             return;
17872         }
17873         
17874         this.collapse();
17875     },
17876
17877     /**
17878      * Returns true if the dropdown list is expanded, else false.
17879      */
17880     isExpanded : function(){
17881         return this.list.isVisible();
17882     },
17883
17884     /**
17885      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17886      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17887      * @param {String} value The data value of the item to select
17888      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17889      * selected item if it is not currently in view (defaults to true)
17890      * @return {Boolean} True if the value matched an item in the list, else false
17891      */
17892     selectByValue : function(v, scrollIntoView){
17893         if(v !== undefined && v !== null){
17894             var r = this.findRecord(this.valueField || this.displayField, v);
17895             if(r){
17896                 this.select(this.store.indexOf(r), scrollIntoView);
17897                 return true;
17898             }
17899         }
17900         return false;
17901     },
17902
17903     /**
17904      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17905      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17906      * @param {Number} index The zero-based index of the list item to select
17907      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17908      * selected item if it is not currently in view (defaults to true)
17909      */
17910     select : function(index, scrollIntoView){
17911         this.selectedIndex = index;
17912         this.view.select(index);
17913         if(scrollIntoView !== false){
17914             var el = this.view.getNode(index);
17915             /*
17916              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17917              */
17918             if(el){
17919                 this.list.scrollChildIntoView(el, false);
17920             }
17921         }
17922     },
17923
17924     // private
17925     selectNext : function(){
17926         var ct = this.store.getCount();
17927         if(ct > 0){
17928             if(this.selectedIndex == -1){
17929                 this.select(0);
17930             }else if(this.selectedIndex < ct-1){
17931                 this.select(this.selectedIndex+1);
17932             }
17933         }
17934     },
17935
17936     // private
17937     selectPrev : function(){
17938         var ct = this.store.getCount();
17939         if(ct > 0){
17940             if(this.selectedIndex == -1){
17941                 this.select(0);
17942             }else if(this.selectedIndex != 0){
17943                 this.select(this.selectedIndex-1);
17944             }
17945         }
17946     },
17947
17948     // private
17949     onKeyUp : function(e){
17950         if(this.editable !== false && !e.isSpecialKey()){
17951             this.lastKey = e.getKey();
17952             this.dqTask.delay(this.queryDelay);
17953         }
17954     },
17955
17956     // private
17957     validateBlur : function(){
17958         return !this.list || !this.list.isVisible();   
17959     },
17960
17961     // private
17962     initQuery : function(){
17963         
17964         var v = this.getRawValue();
17965         
17966         if(this.tickable && this.editable){
17967             v = this.tickableInputEl().getValue();
17968         }
17969         
17970         this.doQuery(v);
17971     },
17972
17973     // private
17974     doForce : function(){
17975         if(this.inputEl().dom.value.length > 0){
17976             this.inputEl().dom.value =
17977                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17978              
17979         }
17980     },
17981
17982     /**
17983      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17984      * query allowing the query action to be canceled if needed.
17985      * @param {String} query The SQL query to execute
17986      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17987      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17988      * saved in the current store (defaults to false)
17989      */
17990     doQuery : function(q, forceAll){
17991         
17992         if(q === undefined || q === null){
17993             q = '';
17994         }
17995         var qe = {
17996             query: q,
17997             forceAll: forceAll,
17998             combo: this,
17999             cancel:false
18000         };
18001         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18002             return false;
18003         }
18004         q = qe.query;
18005         
18006         forceAll = qe.forceAll;
18007         if(forceAll === true || (q.length >= this.minChars)){
18008             
18009             this.hasQuery = true;
18010             
18011             if(this.lastQuery != q || this.alwaysQuery){
18012                 this.lastQuery = q;
18013                 if(this.mode == 'local'){
18014                     this.selectedIndex = -1;
18015                     if(forceAll){
18016                         this.store.clearFilter();
18017                     }else{
18018                         
18019                         if(this.specialFilter){
18020                             this.fireEvent('specialfilter', this);
18021                             this.onLoad();
18022                             return;
18023                         }
18024                         
18025                         this.store.filter(this.displayField, q);
18026                     }
18027                     
18028                     this.store.fireEvent("datachanged", this.store);
18029                     
18030                     this.onLoad();
18031                     
18032                     
18033                 }else{
18034                     
18035                     this.store.baseParams[this.queryParam] = q;
18036                     
18037                     var options = {params : this.getParams(q)};
18038                     
18039                     if(this.loadNext){
18040                         options.add = true;
18041                         options.params.start = this.page * this.pageSize;
18042                     }
18043                     
18044                     this.store.load(options);
18045                     
18046                     /*
18047                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18048                      *  we should expand the list on onLoad
18049                      *  so command out it
18050                      */
18051 //                    this.expand();
18052                 }
18053             }else{
18054                 this.selectedIndex = -1;
18055                 this.onLoad();   
18056             }
18057         }
18058         
18059         this.loadNext = false;
18060     },
18061     
18062     // private
18063     getParams : function(q){
18064         var p = {};
18065         //p[this.queryParam] = q;
18066         
18067         if(this.pageSize){
18068             p.start = 0;
18069             p.limit = this.pageSize;
18070         }
18071         return p;
18072     },
18073
18074     /**
18075      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18076      */
18077     collapse : function(){
18078         if(!this.isExpanded()){
18079             return;
18080         }
18081         
18082         this.list.hide();
18083         
18084         this.hasFocus = false;
18085         
18086         if(this.tickable){
18087             this.okBtn.hide();
18088             this.cancelBtn.hide();
18089             this.trigger.show();
18090             
18091             if(this.editable){
18092                 this.tickableInputEl().dom.value = '';
18093                 this.tickableInputEl().blur();
18094             }
18095             
18096         }
18097         
18098         Roo.get(document).un('mousedown', this.collapseIf, this);
18099         Roo.get(document).un('mousewheel', this.collapseIf, this);
18100         if (!this.editable) {
18101             Roo.get(document).un('keydown', this.listKeyPress, this);
18102         }
18103         this.fireEvent('collapse', this);
18104         
18105         this.validate();
18106     },
18107
18108     // private
18109     collapseIf : function(e){
18110         var in_combo  = e.within(this.el);
18111         var in_list =  e.within(this.list);
18112         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18113         
18114         if (in_combo || in_list || is_list) {
18115             //e.stopPropagation();
18116             return;
18117         }
18118         
18119         if(this.tickable){
18120             this.onTickableFooterButtonClick(e, false, false);
18121         }
18122
18123         this.collapse();
18124         
18125     },
18126
18127     /**
18128      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18129      */
18130     expand : function(){
18131        
18132         if(this.isExpanded() || !this.hasFocus){
18133             return;
18134         }
18135         
18136         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18137         this.list.setWidth(lw);
18138         
18139         Roo.log('expand');
18140         
18141         this.list.show();
18142         
18143         this.restrictHeight();
18144         
18145         if(this.tickable){
18146             
18147             this.tickItems = Roo.apply([], this.item);
18148             
18149             this.okBtn.show();
18150             this.cancelBtn.show();
18151             this.trigger.hide();
18152             
18153             if(this.editable){
18154                 this.tickableInputEl().focus();
18155             }
18156             
18157         }
18158         
18159         Roo.get(document).on('mousedown', this.collapseIf, this);
18160         Roo.get(document).on('mousewheel', this.collapseIf, this);
18161         if (!this.editable) {
18162             Roo.get(document).on('keydown', this.listKeyPress, this);
18163         }
18164         
18165         this.fireEvent('expand', this);
18166     },
18167
18168     // private
18169     // Implements the default empty TriggerField.onTriggerClick function
18170     onTriggerClick : function(e)
18171     {
18172         Roo.log('trigger click');
18173         
18174         if(this.disabled || !this.triggerList){
18175             return;
18176         }
18177         
18178         this.page = 0;
18179         this.loadNext = false;
18180         
18181         if(this.isExpanded()){
18182             this.collapse();
18183             if (!this.blockFocus) {
18184                 this.inputEl().focus();
18185             }
18186             
18187         }else {
18188             this.hasFocus = true;
18189             if(this.triggerAction == 'all') {
18190                 this.doQuery(this.allQuery, true);
18191             } else {
18192                 this.doQuery(this.getRawValue());
18193             }
18194             if (!this.blockFocus) {
18195                 this.inputEl().focus();
18196             }
18197         }
18198     },
18199     
18200     onTickableTriggerClick : function(e)
18201     {
18202         if(this.disabled){
18203             return;
18204         }
18205         
18206         this.page = 0;
18207         this.loadNext = false;
18208         this.hasFocus = true;
18209         
18210         if(this.triggerAction == 'all') {
18211             this.doQuery(this.allQuery, true);
18212         } else {
18213             this.doQuery(this.getRawValue());
18214         }
18215     },
18216     
18217     onSearchFieldClick : function(e)
18218     {
18219         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18220             this.onTickableFooterButtonClick(e, false, false);
18221             return;
18222         }
18223         
18224         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18225             return;
18226         }
18227         
18228         this.page = 0;
18229         this.loadNext = false;
18230         this.hasFocus = true;
18231         
18232         if(this.triggerAction == 'all') {
18233             this.doQuery(this.allQuery, true);
18234         } else {
18235             this.doQuery(this.getRawValue());
18236         }
18237     },
18238     
18239     listKeyPress : function(e)
18240     {
18241         //Roo.log('listkeypress');
18242         // scroll to first matching element based on key pres..
18243         if (e.isSpecialKey()) {
18244             return false;
18245         }
18246         var k = String.fromCharCode(e.getKey()).toUpperCase();
18247         //Roo.log(k);
18248         var match  = false;
18249         var csel = this.view.getSelectedNodes();
18250         var cselitem = false;
18251         if (csel.length) {
18252             var ix = this.view.indexOf(csel[0]);
18253             cselitem  = this.store.getAt(ix);
18254             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18255                 cselitem = false;
18256             }
18257             
18258         }
18259         
18260         this.store.each(function(v) { 
18261             if (cselitem) {
18262                 // start at existing selection.
18263                 if (cselitem.id == v.id) {
18264                     cselitem = false;
18265                 }
18266                 return true;
18267             }
18268                 
18269             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18270                 match = this.store.indexOf(v);
18271                 return false;
18272             }
18273             return true;
18274         }, this);
18275         
18276         if (match === false) {
18277             return true; // no more action?
18278         }
18279         // scroll to?
18280         this.view.select(match);
18281         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18282         sn.scrollIntoView(sn.dom.parentNode, false);
18283     },
18284     
18285     onViewScroll : function(e, t){
18286         
18287         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){
18288             return;
18289         }
18290         
18291         this.hasQuery = true;
18292         
18293         this.loading = this.list.select('.loading', true).first();
18294         
18295         if(this.loading === null){
18296             this.list.createChild({
18297                 tag: 'div',
18298                 cls: 'loading roo-select2-more-results roo-select2-active',
18299                 html: 'Loading more results...'
18300             });
18301             
18302             this.loading = this.list.select('.loading', true).first();
18303             
18304             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18305             
18306             this.loading.hide();
18307         }
18308         
18309         this.loading.show();
18310         
18311         var _combo = this;
18312         
18313         this.page++;
18314         this.loadNext = true;
18315         
18316         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18317         
18318         return;
18319     },
18320     
18321     addItem : function(o)
18322     {   
18323         var dv = ''; // display value
18324         
18325         if (this.displayField) {
18326             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18327         } else {
18328             // this is an error condition!!!
18329             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18330         }
18331         
18332         if(!dv.length){
18333             return;
18334         }
18335         
18336         var choice = this.choices.createChild({
18337             tag: 'li',
18338             cls: 'roo-select2-search-choice',
18339             cn: [
18340                 {
18341                     tag: 'div',
18342                     html: dv
18343                 },
18344                 {
18345                     tag: 'a',
18346                     href: '#',
18347                     cls: 'roo-select2-search-choice-close fa fa-times',
18348                     tabindex: '-1'
18349                 }
18350             ]
18351             
18352         }, this.searchField);
18353         
18354         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18355         
18356         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18357         
18358         this.item.push(o);
18359         
18360         this.lastData = o;
18361         
18362         this.syncValue();
18363         
18364         this.inputEl().dom.value = '';
18365         
18366         this.validate();
18367     },
18368     
18369     onRemoveItem : function(e, _self, o)
18370     {
18371         e.preventDefault();
18372         
18373         this.lastItem = Roo.apply([], this.item);
18374         
18375         var index = this.item.indexOf(o.data) * 1;
18376         
18377         if( index < 0){
18378             Roo.log('not this item?!');
18379             return;
18380         }
18381         
18382         this.item.splice(index, 1);
18383         o.item.remove();
18384         
18385         this.syncValue();
18386         
18387         this.fireEvent('remove', this, e);
18388         
18389         this.validate();
18390         
18391     },
18392     
18393     syncValue : function()
18394     {
18395         if(!this.item.length){
18396             this.clearValue();
18397             return;
18398         }
18399             
18400         var value = [];
18401         var _this = this;
18402         Roo.each(this.item, function(i){
18403             if(_this.valueField){
18404                 value.push(i[_this.valueField]);
18405                 return;
18406             }
18407
18408             value.push(i);
18409         });
18410
18411         this.value = value.join(',');
18412
18413         if(this.hiddenField){
18414             this.hiddenField.dom.value = this.value;
18415         }
18416         
18417         this.store.fireEvent("datachanged", this.store);
18418         
18419         this.validate();
18420     },
18421     
18422     clearItem : function()
18423     {
18424         if(!this.multiple){
18425             return;
18426         }
18427         
18428         this.item = [];
18429         
18430         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18431            c.remove();
18432         });
18433         
18434         this.syncValue();
18435         
18436         this.validate();
18437         
18438         if(this.tickable && !Roo.isTouch){
18439             this.view.refresh();
18440         }
18441     },
18442     
18443     inputEl: function ()
18444     {
18445         if(Roo.isIOS && this.useNativeIOS){
18446             return this.el.select('select.roo-ios-select', true).first();
18447         }
18448         
18449         if(Roo.isTouch && this.mobileTouchView){
18450             return this.el.select('input.form-control',true).first();
18451         }
18452         
18453         if(this.tickable){
18454             return this.searchField;
18455         }
18456         
18457         return this.el.select('input.form-control',true).first();
18458     },
18459     
18460     onTickableFooterButtonClick : function(e, btn, el)
18461     {
18462         e.preventDefault();
18463         
18464         this.lastItem = Roo.apply([], this.item);
18465         
18466         if(btn && btn.name == 'cancel'){
18467             this.tickItems = Roo.apply([], this.item);
18468             this.collapse();
18469             return;
18470         }
18471         
18472         this.clearItem();
18473         
18474         var _this = this;
18475         
18476         Roo.each(this.tickItems, function(o){
18477             _this.addItem(o);
18478         });
18479         
18480         this.collapse();
18481         
18482     },
18483     
18484     validate : function()
18485     {
18486         if(this.getVisibilityEl().hasClass('hidden')){
18487             return true;
18488         }
18489         
18490         var v = this.getRawValue();
18491         
18492         if(this.multiple){
18493             v = this.getValue();
18494         }
18495         
18496         if(this.disabled || this.allowBlank || v.length){
18497             this.markValid();
18498             return true;
18499         }
18500         
18501         this.markInvalid();
18502         return false;
18503     },
18504     
18505     tickableInputEl : function()
18506     {
18507         if(!this.tickable || !this.editable){
18508             return this.inputEl();
18509         }
18510         
18511         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18512     },
18513     
18514     
18515     getAutoCreateTouchView : function()
18516     {
18517         var id = Roo.id();
18518         
18519         var cfg = {
18520             cls: 'form-group' //input-group
18521         };
18522         
18523         var input =  {
18524             tag: 'input',
18525             id : id,
18526             type : this.inputType,
18527             cls : 'form-control x-combo-noedit',
18528             autocomplete: 'new-password',
18529             placeholder : this.placeholder || '',
18530             readonly : true
18531         };
18532         
18533         if (this.name) {
18534             input.name = this.name;
18535         }
18536         
18537         if (this.size) {
18538             input.cls += ' input-' + this.size;
18539         }
18540         
18541         if (this.disabled) {
18542             input.disabled = true;
18543         }
18544         
18545         var inputblock = {
18546             cls : 'roo-combobox-wrap',
18547             cn : [
18548                 input
18549             ]
18550         };
18551         
18552         if(this.before){
18553             inputblock.cls += ' input-group';
18554             
18555             inputblock.cn.unshift({
18556                 tag :'span',
18557                 cls : 'input-group-addon input-group-prepend input-group-text',
18558                 html : this.before
18559             });
18560         }
18561         
18562         if(this.removable && !this.multiple){
18563             inputblock.cls += ' roo-removable';
18564             
18565             inputblock.cn.push({
18566                 tag: 'button',
18567                 html : 'x',
18568                 cls : 'roo-combo-removable-btn close'
18569             });
18570         }
18571
18572         if(this.hasFeedback && !this.allowBlank){
18573             
18574             inputblock.cls += ' has-feedback';
18575             
18576             inputblock.cn.push({
18577                 tag: 'span',
18578                 cls: 'glyphicon form-control-feedback'
18579             });
18580             
18581         }
18582         
18583         if (this.after) {
18584             
18585             inputblock.cls += (this.before) ? '' : ' input-group';
18586             
18587             inputblock.cn.push({
18588                 tag :'span',
18589                 cls : 'input-group-addon input-group-append input-group-text',
18590                 html : this.after
18591             });
18592         }
18593
18594         
18595         var ibwrap = inputblock;
18596         
18597         if(this.multiple){
18598             ibwrap = {
18599                 tag: 'ul',
18600                 cls: 'roo-select2-choices',
18601                 cn:[
18602                     {
18603                         tag: 'li',
18604                         cls: 'roo-select2-search-field',
18605                         cn: [
18606
18607                             inputblock
18608                         ]
18609                     }
18610                 ]
18611             };
18612         
18613             
18614         }
18615         
18616         var combobox = {
18617             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18618             cn: [
18619                 {
18620                     tag: 'input',
18621                     type : 'hidden',
18622                     cls: 'form-hidden-field'
18623                 },
18624                 ibwrap
18625             ]
18626         };
18627         
18628         if(!this.multiple && this.showToggleBtn){
18629             
18630             var caret = {
18631                 cls: 'caret'
18632             };
18633             
18634             if (this.caret != false) {
18635                 caret = {
18636                      tag: 'i',
18637                      cls: 'fa fa-' + this.caret
18638                 };
18639                 
18640             }
18641             
18642             combobox.cn.push({
18643                 tag :'span',
18644                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18645                 cn : [
18646                     Roo.bootstrap.version == 3 ? caret : '',
18647                     {
18648                         tag: 'span',
18649                         cls: 'combobox-clear',
18650                         cn  : [
18651                             {
18652                                 tag : 'i',
18653                                 cls: 'icon-remove'
18654                             }
18655                         ]
18656                     }
18657                 ]
18658
18659             })
18660         }
18661         
18662         if(this.multiple){
18663             combobox.cls += ' roo-select2-container-multi';
18664         }
18665         
18666         var required =  this.allowBlank ?  {
18667                     tag : 'i',
18668                     style: 'display: none'
18669                 } : {
18670                    tag : 'i',
18671                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18672                    tooltip : 'This field is required'
18673                 };
18674         
18675         var align = this.labelAlign || this.parentLabelAlign();
18676         
18677         if (align ==='left' && this.fieldLabel.length) {
18678
18679             cfg.cn = [
18680                 required,
18681                 {
18682                     tag: 'label',
18683                     cls : 'control-label col-form-label',
18684                     html : this.fieldLabel
18685
18686                 },
18687                 {
18688                     cls : 'roo-combobox-wrap ', 
18689                     cn: [
18690                         combobox
18691                     ]
18692                 }
18693             ];
18694             
18695             var labelCfg = cfg.cn[1];
18696             var contentCfg = cfg.cn[2];
18697             
18698
18699             if(this.indicatorpos == 'right'){
18700                 cfg.cn = [
18701                     {
18702                         tag: 'label',
18703                         'for' :  id,
18704                         cls : 'control-label col-form-label',
18705                         cn : [
18706                             {
18707                                 tag : 'span',
18708                                 html : this.fieldLabel
18709                             },
18710                             required
18711                         ]
18712                     },
18713                     {
18714                         cls : "roo-combobox-wrap ",
18715                         cn: [
18716                             combobox
18717                         ]
18718                     }
18719
18720                 ];
18721                 
18722                 labelCfg = cfg.cn[0];
18723                 contentCfg = cfg.cn[1];
18724             }
18725             
18726            
18727             
18728             if(this.labelWidth > 12){
18729                 labelCfg.style = "width: " + this.labelWidth + 'px';
18730             }
18731            
18732             if(this.labelWidth < 13 && this.labelmd == 0){
18733                 this.labelmd = this.labelWidth;
18734             }
18735             
18736             if(this.labellg > 0){
18737                 labelCfg.cls += ' col-lg-' + this.labellg;
18738                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18739             }
18740             
18741             if(this.labelmd > 0){
18742                 labelCfg.cls += ' col-md-' + this.labelmd;
18743                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18744             }
18745             
18746             if(this.labelsm > 0){
18747                 labelCfg.cls += ' col-sm-' + this.labelsm;
18748                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18749             }
18750             
18751             if(this.labelxs > 0){
18752                 labelCfg.cls += ' col-xs-' + this.labelxs;
18753                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18754             }
18755                 
18756                 
18757         } else if ( this.fieldLabel.length) {
18758             cfg.cn = [
18759                required,
18760                 {
18761                     tag: 'label',
18762                     cls : 'control-label',
18763                     html : this.fieldLabel
18764
18765                 },
18766                 {
18767                     cls : '', 
18768                     cn: [
18769                         combobox
18770                     ]
18771                 }
18772             ];
18773             
18774             if(this.indicatorpos == 'right'){
18775                 cfg.cn = [
18776                     {
18777                         tag: 'label',
18778                         cls : 'control-label',
18779                         html : this.fieldLabel,
18780                         cn : [
18781                             required
18782                         ]
18783                     },
18784                     {
18785                         cls : '', 
18786                         cn: [
18787                             combobox
18788                         ]
18789                     }
18790                 ];
18791             }
18792         } else {
18793             cfg.cn = combobox;    
18794         }
18795         
18796         
18797         var settings = this;
18798         
18799         ['xs','sm','md','lg'].map(function(size){
18800             if (settings[size]) {
18801                 cfg.cls += ' col-' + size + '-' + settings[size];
18802             }
18803         });
18804         
18805         return cfg;
18806     },
18807     
18808     initTouchView : function()
18809     {
18810         this.renderTouchView();
18811         
18812         this.touchViewEl.on('scroll', function(){
18813             this.el.dom.scrollTop = 0;
18814         }, this);
18815         
18816         this.originalValue = this.getValue();
18817         
18818         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18819         
18820         this.inputEl().on("click", this.showTouchView, this);
18821         if (this.triggerEl) {
18822             this.triggerEl.on("click", this.showTouchView, this);
18823         }
18824         
18825         
18826         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18827         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18828         
18829         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18830         
18831         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18832         this.store.on('load', this.onTouchViewLoad, this);
18833         this.store.on('loadexception', this.onTouchViewLoadException, this);
18834         
18835         if(this.hiddenName){
18836             
18837             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18838             
18839             this.hiddenField.dom.value =
18840                 this.hiddenValue !== undefined ? this.hiddenValue :
18841                 this.value !== undefined ? this.value : '';
18842         
18843             this.el.dom.removeAttribute('name');
18844             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18845         }
18846         
18847         if(this.multiple){
18848             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18849             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18850         }
18851         
18852         if(this.removable && !this.multiple){
18853             var close = this.closeTriggerEl();
18854             if(close){
18855                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18856                 close.on('click', this.removeBtnClick, this, close);
18857             }
18858         }
18859         /*
18860          * fix the bug in Safari iOS8
18861          */
18862         this.inputEl().on("focus", function(e){
18863             document.activeElement.blur();
18864         }, this);
18865         
18866         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18867         
18868         return;
18869         
18870         
18871     },
18872     
18873     renderTouchView : function()
18874     {
18875         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18876         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18877         
18878         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18879         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880         
18881         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18882         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883         this.touchViewBodyEl.setStyle('overflow', 'auto');
18884         
18885         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18886         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887         
18888         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18889         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         
18891     },
18892     
18893     showTouchView : function()
18894     {
18895         if(this.disabled){
18896             return;
18897         }
18898         
18899         this.touchViewHeaderEl.hide();
18900
18901         if(this.modalTitle.length){
18902             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18903             this.touchViewHeaderEl.show();
18904         }
18905
18906         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18907         this.touchViewEl.show();
18908
18909         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18910         
18911         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18912         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18913
18914         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18915
18916         if(this.modalTitle.length){
18917             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18918         }
18919         
18920         this.touchViewBodyEl.setHeight(bodyHeight);
18921
18922         if(this.animate){
18923             var _this = this;
18924             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18925         }else{
18926             this.touchViewEl.addClass(['in','show']);
18927         }
18928         
18929         if(this._touchViewMask){
18930             Roo.get(document.body).addClass("x-body-masked");
18931             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18932             this._touchViewMask.setStyle('z-index', 10000);
18933             this._touchViewMask.addClass('show');
18934         }
18935         
18936         this.doTouchViewQuery();
18937         
18938     },
18939     
18940     hideTouchView : function()
18941     {
18942         this.touchViewEl.removeClass(['in','show']);
18943
18944         if(this.animate){
18945             var _this = this;
18946             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18947         }else{
18948             this.touchViewEl.setStyle('display', 'none');
18949         }
18950         
18951         if(this._touchViewMask){
18952             this._touchViewMask.removeClass('show');
18953             Roo.get(document.body).removeClass("x-body-masked");
18954         }
18955     },
18956     
18957     setTouchViewValue : function()
18958     {
18959         if(this.multiple){
18960             this.clearItem();
18961         
18962             var _this = this;
18963
18964             Roo.each(this.tickItems, function(o){
18965                 this.addItem(o);
18966             }, this);
18967         }
18968         
18969         this.hideTouchView();
18970     },
18971     
18972     doTouchViewQuery : function()
18973     {
18974         var qe = {
18975             query: '',
18976             forceAll: true,
18977             combo: this,
18978             cancel:false
18979         };
18980         
18981         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18982             return false;
18983         }
18984         
18985         if(!this.alwaysQuery || this.mode == 'local'){
18986             this.onTouchViewLoad();
18987             return;
18988         }
18989         
18990         this.store.load();
18991     },
18992     
18993     onTouchViewBeforeLoad : function(combo,opts)
18994     {
18995         return;
18996     },
18997
18998     // private
18999     onTouchViewLoad : function()
19000     {
19001         if(this.store.getCount() < 1){
19002             this.onTouchViewEmptyResults();
19003             return;
19004         }
19005         
19006         this.clearTouchView();
19007         
19008         var rawValue = this.getRawValue();
19009         
19010         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19011         
19012         this.tickItems = [];
19013         
19014         this.store.data.each(function(d, rowIndex){
19015             var row = this.touchViewListGroup.createChild(template);
19016             
19017             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19018                 row.addClass(d.data.cls);
19019             }
19020             
19021             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19022                 var cfg = {
19023                     data : d.data,
19024                     html : d.data[this.displayField]
19025                 };
19026                 
19027                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19028                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19029                 }
19030             }
19031             row.removeClass('selected');
19032             if(!this.multiple && this.valueField &&
19033                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19034             {
19035                 // radio buttons..
19036                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19037                 row.addClass('selected');
19038             }
19039             
19040             if(this.multiple && this.valueField &&
19041                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19042             {
19043                 
19044                 // checkboxes...
19045                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19046                 this.tickItems.push(d.data);
19047             }
19048             
19049             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19050             
19051         }, this);
19052         
19053         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19054         
19055         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19056
19057         if(this.modalTitle.length){
19058             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19059         }
19060
19061         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19062         
19063         if(this.mobile_restrict_height && listHeight < bodyHeight){
19064             this.touchViewBodyEl.setHeight(listHeight);
19065         }
19066         
19067         var _this = this;
19068         
19069         if(firstChecked && listHeight > bodyHeight){
19070             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19071         }
19072         
19073     },
19074     
19075     onTouchViewLoadException : function()
19076     {
19077         this.hideTouchView();
19078     },
19079     
19080     onTouchViewEmptyResults : function()
19081     {
19082         this.clearTouchView();
19083         
19084         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19085         
19086         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19087         
19088     },
19089     
19090     clearTouchView : function()
19091     {
19092         this.touchViewListGroup.dom.innerHTML = '';
19093     },
19094     
19095     onTouchViewClick : function(e, el, o)
19096     {
19097         e.preventDefault();
19098         
19099         var row = o.row;
19100         var rowIndex = o.rowIndex;
19101         
19102         var r = this.store.getAt(rowIndex);
19103         
19104         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19105             
19106             if(!this.multiple){
19107                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19108                     c.dom.removeAttribute('checked');
19109                 }, this);
19110
19111                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19112
19113                 this.setFromData(r.data);
19114
19115                 var close = this.closeTriggerEl();
19116
19117                 if(close){
19118                     close.show();
19119                 }
19120
19121                 this.hideTouchView();
19122
19123                 this.fireEvent('select', this, r, rowIndex);
19124
19125                 return;
19126             }
19127
19128             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19129                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19130                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19131                 return;
19132             }
19133
19134             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19135             this.addItem(r.data);
19136             this.tickItems.push(r.data);
19137         }
19138     },
19139     
19140     getAutoCreateNativeIOS : function()
19141     {
19142         var cfg = {
19143             cls: 'form-group' //input-group,
19144         };
19145         
19146         var combobox =  {
19147             tag: 'select',
19148             cls : 'roo-ios-select'
19149         };
19150         
19151         if (this.name) {
19152             combobox.name = this.name;
19153         }
19154         
19155         if (this.disabled) {
19156             combobox.disabled = true;
19157         }
19158         
19159         var settings = this;
19160         
19161         ['xs','sm','md','lg'].map(function(size){
19162             if (settings[size]) {
19163                 cfg.cls += ' col-' + size + '-' + settings[size];
19164             }
19165         });
19166         
19167         cfg.cn = combobox;
19168         
19169         return cfg;
19170         
19171     },
19172     
19173     initIOSView : function()
19174     {
19175         this.store.on('load', this.onIOSViewLoad, this);
19176         
19177         return;
19178     },
19179     
19180     onIOSViewLoad : function()
19181     {
19182         if(this.store.getCount() < 1){
19183             return;
19184         }
19185         
19186         this.clearIOSView();
19187         
19188         if(this.allowBlank) {
19189             
19190             var default_text = '-- SELECT --';
19191             
19192             if(this.placeholder.length){
19193                 default_text = this.placeholder;
19194             }
19195             
19196             if(this.emptyTitle.length){
19197                 default_text += ' - ' + this.emptyTitle + ' -';
19198             }
19199             
19200             var opt = this.inputEl().createChild({
19201                 tag: 'option',
19202                 value : 0,
19203                 html : default_text
19204             });
19205             
19206             var o = {};
19207             o[this.valueField] = 0;
19208             o[this.displayField] = default_text;
19209             
19210             this.ios_options.push({
19211                 data : o,
19212                 el : opt
19213             });
19214             
19215         }
19216         
19217         this.store.data.each(function(d, rowIndex){
19218             
19219             var html = '';
19220             
19221             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19222                 html = d.data[this.displayField];
19223             }
19224             
19225             var value = '';
19226             
19227             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19228                 value = d.data[this.valueField];
19229             }
19230             
19231             var option = {
19232                 tag: 'option',
19233                 value : value,
19234                 html : html
19235             };
19236             
19237             if(this.value == d.data[this.valueField]){
19238                 option['selected'] = true;
19239             }
19240             
19241             var opt = this.inputEl().createChild(option);
19242             
19243             this.ios_options.push({
19244                 data : d.data,
19245                 el : opt
19246             });
19247             
19248         }, this);
19249         
19250         this.inputEl().on('change', function(){
19251            this.fireEvent('select', this);
19252         }, this);
19253         
19254     },
19255     
19256     clearIOSView: function()
19257     {
19258         this.inputEl().dom.innerHTML = '';
19259         
19260         this.ios_options = [];
19261     },
19262     
19263     setIOSValue: function(v)
19264     {
19265         this.value = v;
19266         
19267         if(!this.ios_options){
19268             return;
19269         }
19270         
19271         Roo.each(this.ios_options, function(opts){
19272            
19273            opts.el.dom.removeAttribute('selected');
19274            
19275            if(opts.data[this.valueField] != v){
19276                return;
19277            }
19278            
19279            opts.el.dom.setAttribute('selected', true);
19280            
19281         }, this);
19282     }
19283
19284     /** 
19285     * @cfg {Boolean} grow 
19286     * @hide 
19287     */
19288     /** 
19289     * @cfg {Number} growMin 
19290     * @hide 
19291     */
19292     /** 
19293     * @cfg {Number} growMax 
19294     * @hide 
19295     */
19296     /**
19297      * @hide
19298      * @method autoSize
19299      */
19300 });
19301
19302 Roo.apply(Roo.bootstrap.ComboBox,  {
19303     
19304     header : {
19305         tag: 'div',
19306         cls: 'modal-header',
19307         cn: [
19308             {
19309                 tag: 'h4',
19310                 cls: 'modal-title'
19311             }
19312         ]
19313     },
19314     
19315     body : {
19316         tag: 'div',
19317         cls: 'modal-body',
19318         cn: [
19319             {
19320                 tag: 'ul',
19321                 cls: 'list-group'
19322             }
19323         ]
19324     },
19325     
19326     listItemRadio : {
19327         tag: 'li',
19328         cls: 'list-group-item',
19329         cn: [
19330             {
19331                 tag: 'span',
19332                 cls: 'roo-combobox-list-group-item-value'
19333             },
19334             {
19335                 tag: 'div',
19336                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19337                 cn: [
19338                     {
19339                         tag: 'input',
19340                         type: 'radio'
19341                     },
19342                     {
19343                         tag: 'label'
19344                     }
19345                 ]
19346             }
19347         ]
19348     },
19349     
19350     listItemCheckbox : {
19351         tag: 'li',
19352         cls: 'list-group-item',
19353         cn: [
19354             {
19355                 tag: 'span',
19356                 cls: 'roo-combobox-list-group-item-value'
19357             },
19358             {
19359                 tag: 'div',
19360                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19361                 cn: [
19362                     {
19363                         tag: 'input',
19364                         type: 'checkbox'
19365                     },
19366                     {
19367                         tag: 'label'
19368                     }
19369                 ]
19370             }
19371         ]
19372     },
19373     
19374     emptyResult : {
19375         tag: 'div',
19376         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19377     },
19378     
19379     footer : {
19380         tag: 'div',
19381         cls: 'modal-footer',
19382         cn: [
19383             {
19384                 tag: 'div',
19385                 cls: 'row',
19386                 cn: [
19387                     {
19388                         tag: 'div',
19389                         cls: 'col-xs-6 text-left',
19390                         cn: {
19391                             tag: 'button',
19392                             cls: 'btn btn-danger roo-touch-view-cancel',
19393                             html: 'Cancel'
19394                         }
19395                     },
19396                     {
19397                         tag: 'div',
19398                         cls: 'col-xs-6 text-right',
19399                         cn: {
19400                             tag: 'button',
19401                             cls: 'btn btn-success roo-touch-view-ok',
19402                             html: 'OK'
19403                         }
19404                     }
19405                 ]
19406             }
19407         ]
19408         
19409     }
19410 });
19411
19412 Roo.apply(Roo.bootstrap.ComboBox,  {
19413     
19414     touchViewTemplate : {
19415         tag: 'div',
19416         cls: 'modal fade roo-combobox-touch-view',
19417         cn: [
19418             {
19419                 tag: 'div',
19420                 cls: 'modal-dialog',
19421                 style : 'position:fixed', // we have to fix position....
19422                 cn: [
19423                     {
19424                         tag: 'div',
19425                         cls: 'modal-content',
19426                         cn: [
19427                             Roo.bootstrap.ComboBox.header,
19428                             Roo.bootstrap.ComboBox.body,
19429                             Roo.bootstrap.ComboBox.footer
19430                         ]
19431                     }
19432                 ]
19433             }
19434         ]
19435     }
19436 });/*
19437  * Based on:
19438  * Ext JS Library 1.1.1
19439  * Copyright(c) 2006-2007, Ext JS, LLC.
19440  *
19441  * Originally Released Under LGPL - original licence link has changed is not relivant.
19442  *
19443  * Fork - LGPL
19444  * <script type="text/javascript">
19445  */
19446
19447 /**
19448  * @class Roo.View
19449  * @extends Roo.util.Observable
19450  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19451  * This class also supports single and multi selection modes. <br>
19452  * Create a data model bound view:
19453  <pre><code>
19454  var store = new Roo.data.Store(...);
19455
19456  var view = new Roo.View({
19457     el : "my-element",
19458     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19459  
19460     singleSelect: true,
19461     selectedClass: "ydataview-selected",
19462     store: store
19463  });
19464
19465  // listen for node click?
19466  view.on("click", function(vw, index, node, e){
19467  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19468  });
19469
19470  // load XML data
19471  dataModel.load("foobar.xml");
19472  </code></pre>
19473  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19474  * <br><br>
19475  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19476  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19477  * 
19478  * Note: old style constructor is still suported (container, template, config)
19479  * 
19480  * @constructor
19481  * Create a new View
19482  * @param {Object} config The config object
19483  * 
19484  */
19485 Roo.View = function(config, depreciated_tpl, depreciated_config){
19486     
19487     this.parent = false;
19488     
19489     if (typeof(depreciated_tpl) == 'undefined') {
19490         // new way.. - universal constructor.
19491         Roo.apply(this, config);
19492         this.el  = Roo.get(this.el);
19493     } else {
19494         // old format..
19495         this.el  = Roo.get(config);
19496         this.tpl = depreciated_tpl;
19497         Roo.apply(this, depreciated_config);
19498     }
19499     this.wrapEl  = this.el.wrap().wrap();
19500     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19501     
19502     
19503     if(typeof(this.tpl) == "string"){
19504         this.tpl = new Roo.Template(this.tpl);
19505     } else {
19506         // support xtype ctors..
19507         this.tpl = new Roo.factory(this.tpl, Roo);
19508     }
19509     
19510     
19511     this.tpl.compile();
19512     
19513     /** @private */
19514     this.addEvents({
19515         /**
19516          * @event beforeclick
19517          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
19524         /**
19525          * @event click
19526          * Fires when a template node is 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             "click" : true,
19533         /**
19534          * @event dblclick
19535          * Fires when a template node is double 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             "dblclick" : true,
19542         /**
19543          * @event contextmenu
19544          * Fires when a template node is right clicked.
19545          * @param {Roo.View} this
19546          * @param {Number} index The index of the target node
19547          * @param {HTMLElement} node The target node
19548          * @param {Roo.EventObject} e The raw event object
19549          */
19550             "contextmenu" : true,
19551         /**
19552          * @event selectionchange
19553          * Fires when the selected nodes change.
19554          * @param {Roo.View} this
19555          * @param {Array} selections Array of the selected nodes
19556          */
19557             "selectionchange" : true,
19558     
19559         /**
19560          * @event beforeselect
19561          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19562          * @param {Roo.View} this
19563          * @param {HTMLElement} node The node to be selected
19564          * @param {Array} selections Array of currently selected nodes
19565          */
19566             "beforeselect" : true,
19567         /**
19568          * @event preparedata
19569          * Fires on every row to render, to allow you to change the data.
19570          * @param {Roo.View} this
19571          * @param {Object} data to be rendered (change this)
19572          */
19573           "preparedata" : true
19574           
19575           
19576         });
19577
19578
19579
19580     this.el.on({
19581         "click": this.onClick,
19582         "dblclick": this.onDblClick,
19583         "contextmenu": this.onContextMenu,
19584         scope:this
19585     });
19586
19587     this.selections = [];
19588     this.nodes = [];
19589     this.cmp = new Roo.CompositeElementLite([]);
19590     if(this.store){
19591         this.store = Roo.factory(this.store, Roo.data);
19592         this.setStore(this.store, true);
19593     }
19594     
19595     if ( this.footer && this.footer.xtype) {
19596            
19597          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19598         
19599         this.footer.dataSource = this.store;
19600         this.footer.container = fctr;
19601         this.footer = Roo.factory(this.footer, Roo);
19602         fctr.insertFirst(this.el);
19603         
19604         // this is a bit insane - as the paging toolbar seems to detach the el..
19605 //        dom.parentNode.parentNode.parentNode
19606          // they get detached?
19607     }
19608     
19609     
19610     Roo.View.superclass.constructor.call(this);
19611     
19612     
19613 };
19614
19615 Roo.extend(Roo.View, Roo.util.Observable, {
19616     
19617      /**
19618      * @cfg {Roo.data.Store} store Data store to load data from.
19619      */
19620     store : false,
19621     
19622     /**
19623      * @cfg {String|Roo.Element} el The container element.
19624      */
19625     el : '',
19626     
19627     /**
19628      * @cfg {String|Roo.Template} tpl The template used by this View 
19629      */
19630     tpl : false,
19631     /**
19632      * @cfg {String} dataName the named area of the template to use as the data area
19633      *                          Works with domtemplates roo-name="name"
19634      */
19635     dataName: false,
19636     /**
19637      * @cfg {String} selectedClass The css class to add to selected nodes
19638      */
19639     selectedClass : "x-view-selected",
19640      /**
19641      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19642      */
19643     emptyText : "",
19644     
19645     /**
19646      * @cfg {String} text to display on mask (default Loading)
19647      */
19648     mask : false,
19649     /**
19650      * @cfg {Boolean} multiSelect Allow multiple selection
19651      */
19652     multiSelect : false,
19653     /**
19654      * @cfg {Boolean} singleSelect Allow single selection
19655      */
19656     singleSelect:  false,
19657     
19658     /**
19659      * @cfg {Boolean} toggleSelect - selecting 
19660      */
19661     toggleSelect : false,
19662     
19663     /**
19664      * @cfg {Boolean} tickable - selecting 
19665      */
19666     tickable : false,
19667     
19668     /**
19669      * Returns the element this view is bound to.
19670      * @return {Roo.Element}
19671      */
19672     getEl : function(){
19673         return this.wrapEl;
19674     },
19675     
19676     
19677
19678     /**
19679      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19680      */
19681     refresh : function(){
19682         //Roo.log('refresh');
19683         var t = this.tpl;
19684         
19685         // if we are using something like 'domtemplate', then
19686         // the what gets used is:
19687         // t.applySubtemplate(NAME, data, wrapping data..)
19688         // the outer template then get' applied with
19689         //     the store 'extra data'
19690         // and the body get's added to the
19691         //      roo-name="data" node?
19692         //      <span class='roo-tpl-{name}'></span> ?????
19693         
19694         
19695         
19696         this.clearSelections();
19697         this.el.update("");
19698         var html = [];
19699         var records = this.store.getRange();
19700         if(records.length < 1) {
19701             
19702             // is this valid??  = should it render a template??
19703             
19704             this.el.update(this.emptyText);
19705             return;
19706         }
19707         var el = this.el;
19708         if (this.dataName) {
19709             this.el.update(t.apply(this.store.meta)); //????
19710             el = this.el.child('.roo-tpl-' + this.dataName);
19711         }
19712         
19713         for(var i = 0, len = records.length; i < len; i++){
19714             var data = this.prepareData(records[i].data, i, records[i]);
19715             this.fireEvent("preparedata", this, data, i, records[i]);
19716             
19717             var d = Roo.apply({}, data);
19718             
19719             if(this.tickable){
19720                 Roo.apply(d, {'roo-id' : Roo.id()});
19721                 
19722                 var _this = this;
19723             
19724                 Roo.each(this.parent.item, function(item){
19725                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19726                         return;
19727                     }
19728                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19729                 });
19730             }
19731             
19732             html[html.length] = Roo.util.Format.trim(
19733                 this.dataName ?
19734                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19735                     t.apply(d)
19736             );
19737         }
19738         
19739         
19740         
19741         el.update(html.join(""));
19742         this.nodes = el.dom.childNodes;
19743         this.updateIndexes(0);
19744     },
19745     
19746
19747     /**
19748      * Function to override to reformat the data that is sent to
19749      * the template for each node.
19750      * DEPRICATED - use the preparedata event handler.
19751      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19752      * a JSON object for an UpdateManager bound view).
19753      */
19754     prepareData : function(data, index, record)
19755     {
19756         this.fireEvent("preparedata", this, data, index, record);
19757         return data;
19758     },
19759
19760     onUpdate : function(ds, record){
19761         // Roo.log('on update');   
19762         this.clearSelections();
19763         var index = this.store.indexOf(record);
19764         var n = this.nodes[index];
19765         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19766         n.parentNode.removeChild(n);
19767         this.updateIndexes(index, index);
19768     },
19769
19770     
19771     
19772 // --------- FIXME     
19773     onAdd : function(ds, records, index)
19774     {
19775         //Roo.log(['on Add', ds, records, index] );        
19776         this.clearSelections();
19777         if(this.nodes.length == 0){
19778             this.refresh();
19779             return;
19780         }
19781         var n = this.nodes[index];
19782         for(var i = 0, len = records.length; i < len; i++){
19783             var d = this.prepareData(records[i].data, i, records[i]);
19784             if(n){
19785                 this.tpl.insertBefore(n, d);
19786             }else{
19787                 
19788                 this.tpl.append(this.el, d);
19789             }
19790         }
19791         this.updateIndexes(index);
19792     },
19793
19794     onRemove : function(ds, record, index){
19795        // Roo.log('onRemove');
19796         this.clearSelections();
19797         var el = this.dataName  ?
19798             this.el.child('.roo-tpl-' + this.dataName) :
19799             this.el; 
19800         
19801         el.dom.removeChild(this.nodes[index]);
19802         this.updateIndexes(index);
19803     },
19804
19805     /**
19806      * Refresh an individual node.
19807      * @param {Number} index
19808      */
19809     refreshNode : function(index){
19810         this.onUpdate(this.store, this.store.getAt(index));
19811     },
19812
19813     updateIndexes : function(startIndex, endIndex){
19814         var ns = this.nodes;
19815         startIndex = startIndex || 0;
19816         endIndex = endIndex || ns.length - 1;
19817         for(var i = startIndex; i <= endIndex; i++){
19818             ns[i].nodeIndex = i;
19819         }
19820     },
19821
19822     /**
19823      * Changes the data store this view uses and refresh the view.
19824      * @param {Store} store
19825      */
19826     setStore : function(store, initial){
19827         if(!initial && this.store){
19828             this.store.un("datachanged", this.refresh);
19829             this.store.un("add", this.onAdd);
19830             this.store.un("remove", this.onRemove);
19831             this.store.un("update", this.onUpdate);
19832             this.store.un("clear", this.refresh);
19833             this.store.un("beforeload", this.onBeforeLoad);
19834             this.store.un("load", this.onLoad);
19835             this.store.un("loadexception", this.onLoad);
19836         }
19837         if(store){
19838           
19839             store.on("datachanged", this.refresh, this);
19840             store.on("add", this.onAdd, this);
19841             store.on("remove", this.onRemove, this);
19842             store.on("update", this.onUpdate, this);
19843             store.on("clear", this.refresh, this);
19844             store.on("beforeload", this.onBeforeLoad, this);
19845             store.on("load", this.onLoad, this);
19846             store.on("loadexception", this.onLoad, this);
19847         }
19848         
19849         if(store){
19850             this.refresh();
19851         }
19852     },
19853     /**
19854      * onbeforeLoad - masks the loading area.
19855      *
19856      */
19857     onBeforeLoad : function(store,opts)
19858     {
19859          //Roo.log('onBeforeLoad');   
19860         if (!opts.add) {
19861             this.el.update("");
19862         }
19863         this.el.mask(this.mask ? this.mask : "Loading" ); 
19864     },
19865     onLoad : function ()
19866     {
19867         this.el.unmask();
19868     },
19869     
19870
19871     /**
19872      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19873      * @param {HTMLElement} node
19874      * @return {HTMLElement} The template node
19875      */
19876     findItemFromChild : function(node){
19877         var el = this.dataName  ?
19878             this.el.child('.roo-tpl-' + this.dataName,true) :
19879             this.el.dom; 
19880         
19881         if(!node || node.parentNode == el){
19882                     return node;
19883             }
19884             var p = node.parentNode;
19885             while(p && p != el){
19886             if(p.parentNode == el){
19887                 return p;
19888             }
19889             p = p.parentNode;
19890         }
19891             return null;
19892     },
19893
19894     /** @ignore */
19895     onClick : function(e){
19896         var item = this.findItemFromChild(e.getTarget());
19897         if(item){
19898             var index = this.indexOf(item);
19899             if(this.onItemClick(item, index, e) !== false){
19900                 this.fireEvent("click", this, index, item, e);
19901             }
19902         }else{
19903             this.clearSelections();
19904         }
19905     },
19906
19907     /** @ignore */
19908     onContextMenu : function(e){
19909         var item = this.findItemFromChild(e.getTarget());
19910         if(item){
19911             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19912         }
19913     },
19914
19915     /** @ignore */
19916     onDblClick : function(e){
19917         var item = this.findItemFromChild(e.getTarget());
19918         if(item){
19919             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19920         }
19921     },
19922
19923     onItemClick : function(item, index, e)
19924     {
19925         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19926             return false;
19927         }
19928         if (this.toggleSelect) {
19929             var m = this.isSelected(item) ? 'unselect' : 'select';
19930             //Roo.log(m);
19931             var _t = this;
19932             _t[m](item, true, false);
19933             return true;
19934         }
19935         if(this.multiSelect || this.singleSelect){
19936             if(this.multiSelect && e.shiftKey && this.lastSelection){
19937                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19938             }else{
19939                 this.select(item, this.multiSelect && e.ctrlKey);
19940                 this.lastSelection = item;
19941             }
19942             
19943             if(!this.tickable){
19944                 e.preventDefault();
19945             }
19946             
19947         }
19948         return true;
19949     },
19950
19951     /**
19952      * Get the number of selected nodes.
19953      * @return {Number}
19954      */
19955     getSelectionCount : function(){
19956         return this.selections.length;
19957     },
19958
19959     /**
19960      * Get the currently selected nodes.
19961      * @return {Array} An array of HTMLElements
19962      */
19963     getSelectedNodes : function(){
19964         return this.selections;
19965     },
19966
19967     /**
19968      * Get the indexes of the selected nodes.
19969      * @return {Array}
19970      */
19971     getSelectedIndexes : function(){
19972         var indexes = [], s = this.selections;
19973         for(var i = 0, len = s.length; i < len; i++){
19974             indexes.push(s[i].nodeIndex);
19975         }
19976         return indexes;
19977     },
19978
19979     /**
19980      * Clear all selections
19981      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19982      */
19983     clearSelections : function(suppressEvent){
19984         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19985             this.cmp.elements = this.selections;
19986             this.cmp.removeClass(this.selectedClass);
19987             this.selections = [];
19988             if(!suppressEvent){
19989                 this.fireEvent("selectionchange", this, this.selections);
19990             }
19991         }
19992     },
19993
19994     /**
19995      * Returns true if the passed node is selected
19996      * @param {HTMLElement/Number} node The node or node index
19997      * @return {Boolean}
19998      */
19999     isSelected : function(node){
20000         var s = this.selections;
20001         if(s.length < 1){
20002             return false;
20003         }
20004         node = this.getNode(node);
20005         return s.indexOf(node) !== -1;
20006     },
20007
20008     /**
20009      * Selects nodes.
20010      * @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
20011      * @param {Boolean} keepExisting (optional) true to keep existing selections
20012      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20013      */
20014     select : function(nodeInfo, keepExisting, suppressEvent){
20015         if(nodeInfo instanceof Array){
20016             if(!keepExisting){
20017                 this.clearSelections(true);
20018             }
20019             for(var i = 0, len = nodeInfo.length; i < len; i++){
20020                 this.select(nodeInfo[i], true, true);
20021             }
20022             return;
20023         } 
20024         var node = this.getNode(nodeInfo);
20025         if(!node || this.isSelected(node)){
20026             return; // already selected.
20027         }
20028         if(!keepExisting){
20029             this.clearSelections(true);
20030         }
20031         
20032         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20033             Roo.fly(node).addClass(this.selectedClass);
20034             this.selections.push(node);
20035             if(!suppressEvent){
20036                 this.fireEvent("selectionchange", this, this.selections);
20037             }
20038         }
20039         
20040         
20041     },
20042       /**
20043      * Unselects nodes.
20044      * @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
20045      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20046      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20047      */
20048     unselect : function(nodeInfo, keepExisting, suppressEvent)
20049     {
20050         if(nodeInfo instanceof Array){
20051             Roo.each(this.selections, function(s) {
20052                 this.unselect(s, nodeInfo);
20053             }, this);
20054             return;
20055         }
20056         var node = this.getNode(nodeInfo);
20057         if(!node || !this.isSelected(node)){
20058             //Roo.log("not selected");
20059             return; // not selected.
20060         }
20061         // fireevent???
20062         var ns = [];
20063         Roo.each(this.selections, function(s) {
20064             if (s == node ) {
20065                 Roo.fly(node).removeClass(this.selectedClass);
20066
20067                 return;
20068             }
20069             ns.push(s);
20070         },this);
20071         
20072         this.selections= ns;
20073         this.fireEvent("selectionchange", this, this.selections);
20074     },
20075
20076     /**
20077      * Gets a template node.
20078      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20079      * @return {HTMLElement} The node or null if it wasn't found
20080      */
20081     getNode : function(nodeInfo){
20082         if(typeof nodeInfo == "string"){
20083             return document.getElementById(nodeInfo);
20084         }else if(typeof nodeInfo == "number"){
20085             return this.nodes[nodeInfo];
20086         }
20087         return nodeInfo;
20088     },
20089
20090     /**
20091      * Gets a range template nodes.
20092      * @param {Number} startIndex
20093      * @param {Number} endIndex
20094      * @return {Array} An array of nodes
20095      */
20096     getNodes : function(start, end){
20097         var ns = this.nodes;
20098         start = start || 0;
20099         end = typeof end == "undefined" ? ns.length - 1 : end;
20100         var nodes = [];
20101         if(start <= end){
20102             for(var i = start; i <= end; i++){
20103                 nodes.push(ns[i]);
20104             }
20105         } else{
20106             for(var i = start; i >= end; i--){
20107                 nodes.push(ns[i]);
20108             }
20109         }
20110         return nodes;
20111     },
20112
20113     /**
20114      * Finds the index of the passed node
20115      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20116      * @return {Number} The index of the node or -1
20117      */
20118     indexOf : function(node){
20119         node = this.getNode(node);
20120         if(typeof node.nodeIndex == "number"){
20121             return node.nodeIndex;
20122         }
20123         var ns = this.nodes;
20124         for(var i = 0, len = ns.length; i < len; i++){
20125             if(ns[i] == node){
20126                 return i;
20127             }
20128         }
20129         return -1;
20130     }
20131 });
20132 /*
20133  * - LGPL
20134  *
20135  * based on jquery fullcalendar
20136  * 
20137  */
20138
20139 Roo.bootstrap = Roo.bootstrap || {};
20140 /**
20141  * @class Roo.bootstrap.Calendar
20142  * @extends Roo.bootstrap.Component
20143  * Bootstrap Calendar class
20144  * @cfg {Boolean} loadMask (true|false) default false
20145  * @cfg {Object} header generate the user specific header of the calendar, default false
20146
20147  * @constructor
20148  * Create a new Container
20149  * @param {Object} config The config object
20150  */
20151
20152
20153
20154 Roo.bootstrap.Calendar = function(config){
20155     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20156      this.addEvents({
20157         /**
20158              * @event select
20159              * Fires when a date is selected
20160              * @param {DatePicker} this
20161              * @param {Date} date The selected date
20162              */
20163         'select': true,
20164         /**
20165              * @event monthchange
20166              * Fires when the displayed month changes 
20167              * @param {DatePicker} this
20168              * @param {Date} date The selected month
20169              */
20170         'monthchange': true,
20171         /**
20172              * @event evententer
20173              * Fires when mouse over an event
20174              * @param {Calendar} this
20175              * @param {event} Event
20176              */
20177         'evententer': true,
20178         /**
20179              * @event eventleave
20180              * Fires when the mouse leaves an
20181              * @param {Calendar} this
20182              * @param {event}
20183              */
20184         'eventleave': true,
20185         /**
20186              * @event eventclick
20187              * Fires when the mouse click an
20188              * @param {Calendar} this
20189              * @param {event}
20190              */
20191         'eventclick': true
20192         
20193     });
20194
20195 };
20196
20197 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20198     
20199      /**
20200      * @cfg {Number} startDay
20201      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20202      */
20203     startDay : 0,
20204     
20205     loadMask : false,
20206     
20207     header : false,
20208       
20209     getAutoCreate : function(){
20210         
20211         
20212         var fc_button = function(name, corner, style, content ) {
20213             return Roo.apply({},{
20214                 tag : 'span',
20215                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20216                          (corner.length ?
20217                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20218                             ''
20219                         ),
20220                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20221                 unselectable: 'on'
20222             });
20223         };
20224         
20225         var header = {};
20226         
20227         if(!this.header){
20228             header = {
20229                 tag : 'table',
20230                 cls : 'fc-header',
20231                 style : 'width:100%',
20232                 cn : [
20233                     {
20234                         tag: 'tr',
20235                         cn : [
20236                             {
20237                                 tag : 'td',
20238                                 cls : 'fc-header-left',
20239                                 cn : [
20240                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20241                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20242                                     { tag: 'span', cls: 'fc-header-space' },
20243                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20244
20245
20246                                 ]
20247                             },
20248
20249                             {
20250                                 tag : 'td',
20251                                 cls : 'fc-header-center',
20252                                 cn : [
20253                                     {
20254                                         tag: 'span',
20255                                         cls: 'fc-header-title',
20256                                         cn : {
20257                                             tag: 'H2',
20258                                             html : 'month / year'
20259                                         }
20260                                     }
20261
20262                                 ]
20263                             },
20264                             {
20265                                 tag : 'td',
20266                                 cls : 'fc-header-right',
20267                                 cn : [
20268                               /*      fc_button('month', 'left', '', 'month' ),
20269                                     fc_button('week', '', '', 'week' ),
20270                                     fc_button('day', 'right', '', 'day' )
20271                                 */    
20272
20273                                 ]
20274                             }
20275
20276                         ]
20277                     }
20278                 ]
20279             };
20280         }
20281         
20282         header = this.header;
20283         
20284        
20285         var cal_heads = function() {
20286             var ret = [];
20287             // fixme - handle this.
20288             
20289             for (var i =0; i < Date.dayNames.length; i++) {
20290                 var d = Date.dayNames[i];
20291                 ret.push({
20292                     tag: 'th',
20293                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20294                     html : d.substring(0,3)
20295                 });
20296                 
20297             }
20298             ret[0].cls += ' fc-first';
20299             ret[6].cls += ' fc-last';
20300             return ret;
20301         };
20302         var cal_cell = function(n) {
20303             return  {
20304                 tag: 'td',
20305                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20306                 cn : [
20307                     {
20308                         cn : [
20309                             {
20310                                 cls: 'fc-day-number',
20311                                 html: 'D'
20312                             },
20313                             {
20314                                 cls: 'fc-day-content',
20315                              
20316                                 cn : [
20317                                      {
20318                                         style: 'position: relative;' // height: 17px;
20319                                     }
20320                                 ]
20321                             }
20322                             
20323                             
20324                         ]
20325                     }
20326                 ]
20327                 
20328             }
20329         };
20330         var cal_rows = function() {
20331             
20332             var ret = [];
20333             for (var r = 0; r < 6; r++) {
20334                 var row= {
20335                     tag : 'tr',
20336                     cls : 'fc-week',
20337                     cn : []
20338                 };
20339                 
20340                 for (var i =0; i < Date.dayNames.length; i++) {
20341                     var d = Date.dayNames[i];
20342                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20343
20344                 }
20345                 row.cn[0].cls+=' fc-first';
20346                 row.cn[0].cn[0].style = 'min-height:90px';
20347                 row.cn[6].cls+=' fc-last';
20348                 ret.push(row);
20349                 
20350             }
20351             ret[0].cls += ' fc-first';
20352             ret[4].cls += ' fc-prev-last';
20353             ret[5].cls += ' fc-last';
20354             return ret;
20355             
20356         };
20357         
20358         var cal_table = {
20359             tag: 'table',
20360             cls: 'fc-border-separate',
20361             style : 'width:100%',
20362             cellspacing  : 0,
20363             cn : [
20364                 { 
20365                     tag: 'thead',
20366                     cn : [
20367                         { 
20368                             tag: 'tr',
20369                             cls : 'fc-first fc-last',
20370                             cn : cal_heads()
20371                         }
20372                     ]
20373                 },
20374                 { 
20375                     tag: 'tbody',
20376                     cn : cal_rows()
20377                 }
20378                   
20379             ]
20380         };
20381          
20382          var cfg = {
20383             cls : 'fc fc-ltr',
20384             cn : [
20385                 header,
20386                 {
20387                     cls : 'fc-content',
20388                     style : "position: relative;",
20389                     cn : [
20390                         {
20391                             cls : 'fc-view fc-view-month fc-grid',
20392                             style : 'position: relative',
20393                             unselectable : 'on',
20394                             cn : [
20395                                 {
20396                                     cls : 'fc-event-container',
20397                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20398                                 },
20399                                 cal_table
20400                             ]
20401                         }
20402                     ]
20403     
20404                 }
20405            ] 
20406             
20407         };
20408         
20409          
20410         
20411         return cfg;
20412     },
20413     
20414     
20415     initEvents : function()
20416     {
20417         if(!this.store){
20418             throw "can not find store for calendar";
20419         }
20420         
20421         var mark = {
20422             tag: "div",
20423             cls:"x-dlg-mask",
20424             style: "text-align:center",
20425             cn: [
20426                 {
20427                     tag: "div",
20428                     style: "background-color:white;width:50%;margin:250 auto",
20429                     cn: [
20430                         {
20431                             tag: "img",
20432                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20433                         },
20434                         {
20435                             tag: "span",
20436                             html: "Loading"
20437                         }
20438                         
20439                     ]
20440                 }
20441             ]
20442         };
20443         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20444         
20445         var size = this.el.select('.fc-content', true).first().getSize();
20446         this.maskEl.setSize(size.width, size.height);
20447         this.maskEl.enableDisplayMode("block");
20448         if(!this.loadMask){
20449             this.maskEl.hide();
20450         }
20451         
20452         this.store = Roo.factory(this.store, Roo.data);
20453         this.store.on('load', this.onLoad, this);
20454         this.store.on('beforeload', this.onBeforeLoad, this);
20455         
20456         this.resize();
20457         
20458         this.cells = this.el.select('.fc-day',true);
20459         //Roo.log(this.cells);
20460         this.textNodes = this.el.query('.fc-day-number');
20461         this.cells.addClassOnOver('fc-state-hover');
20462         
20463         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20464         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20465         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20466         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20467         
20468         this.on('monthchange', this.onMonthChange, this);
20469         
20470         this.update(new Date().clearTime());
20471     },
20472     
20473     resize : function() {
20474         var sz  = this.el.getSize();
20475         
20476         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20477         this.el.select('.fc-day-content div',true).setHeight(34);
20478     },
20479     
20480     
20481     // private
20482     showPrevMonth : function(e){
20483         this.update(this.activeDate.add("mo", -1));
20484     },
20485     showToday : function(e){
20486         this.update(new Date().clearTime());
20487     },
20488     // private
20489     showNextMonth : function(e){
20490         this.update(this.activeDate.add("mo", 1));
20491     },
20492
20493     // private
20494     showPrevYear : function(){
20495         this.update(this.activeDate.add("y", -1));
20496     },
20497
20498     // private
20499     showNextYear : function(){
20500         this.update(this.activeDate.add("y", 1));
20501     },
20502
20503     
20504    // private
20505     update : function(date)
20506     {
20507         var vd = this.activeDate;
20508         this.activeDate = date;
20509 //        if(vd && this.el){
20510 //            var t = date.getTime();
20511 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20512 //                Roo.log('using add remove');
20513 //                
20514 //                this.fireEvent('monthchange', this, date);
20515 //                
20516 //                this.cells.removeClass("fc-state-highlight");
20517 //                this.cells.each(function(c){
20518 //                   if(c.dateValue == t){
20519 //                       c.addClass("fc-state-highlight");
20520 //                       setTimeout(function(){
20521 //                            try{c.dom.firstChild.focus();}catch(e){}
20522 //                       }, 50);
20523 //                       return false;
20524 //                   }
20525 //                   return true;
20526 //                });
20527 //                return;
20528 //            }
20529 //        }
20530         
20531         var days = date.getDaysInMonth();
20532         
20533         var firstOfMonth = date.getFirstDateOfMonth();
20534         var startingPos = firstOfMonth.getDay()-this.startDay;
20535         
20536         if(startingPos < this.startDay){
20537             startingPos += 7;
20538         }
20539         
20540         var pm = date.add(Date.MONTH, -1);
20541         var prevStart = pm.getDaysInMonth()-startingPos;
20542 //        
20543         this.cells = this.el.select('.fc-day',true);
20544         this.textNodes = this.el.query('.fc-day-number');
20545         this.cells.addClassOnOver('fc-state-hover');
20546         
20547         var cells = this.cells.elements;
20548         var textEls = this.textNodes;
20549         
20550         Roo.each(cells, function(cell){
20551             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20552         });
20553         
20554         days += startingPos;
20555
20556         // convert everything to numbers so it's fast
20557         var day = 86400000;
20558         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20559         //Roo.log(d);
20560         //Roo.log(pm);
20561         //Roo.log(prevStart);
20562         
20563         var today = new Date().clearTime().getTime();
20564         var sel = date.clearTime().getTime();
20565         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20566         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20567         var ddMatch = this.disabledDatesRE;
20568         var ddText = this.disabledDatesText;
20569         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20570         var ddaysText = this.disabledDaysText;
20571         var format = this.format;
20572         
20573         var setCellClass = function(cal, cell){
20574             cell.row = 0;
20575             cell.events = [];
20576             cell.more = [];
20577             //Roo.log('set Cell Class');
20578             cell.title = "";
20579             var t = d.getTime();
20580             
20581             //Roo.log(d);
20582             
20583             cell.dateValue = t;
20584             if(t == today){
20585                 cell.className += " fc-today";
20586                 cell.className += " fc-state-highlight";
20587                 cell.title = cal.todayText;
20588             }
20589             if(t == sel){
20590                 // disable highlight in other month..
20591                 //cell.className += " fc-state-highlight";
20592                 
20593             }
20594             // disabling
20595             if(t < min) {
20596                 cell.className = " fc-state-disabled";
20597                 cell.title = cal.minText;
20598                 return;
20599             }
20600             if(t > max) {
20601                 cell.className = " fc-state-disabled";
20602                 cell.title = cal.maxText;
20603                 return;
20604             }
20605             if(ddays){
20606                 if(ddays.indexOf(d.getDay()) != -1){
20607                     cell.title = ddaysText;
20608                     cell.className = " fc-state-disabled";
20609                 }
20610             }
20611             if(ddMatch && format){
20612                 var fvalue = d.dateFormat(format);
20613                 if(ddMatch.test(fvalue)){
20614                     cell.title = ddText.replace("%0", fvalue);
20615                     cell.className = " fc-state-disabled";
20616                 }
20617             }
20618             
20619             if (!cell.initialClassName) {
20620                 cell.initialClassName = cell.dom.className;
20621             }
20622             
20623             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20624         };
20625
20626         var i = 0;
20627         
20628         for(; i < startingPos; i++) {
20629             textEls[i].innerHTML = (++prevStart);
20630             d.setDate(d.getDate()+1);
20631             
20632             cells[i].className = "fc-past fc-other-month";
20633             setCellClass(this, cells[i]);
20634         }
20635         
20636         var intDay = 0;
20637         
20638         for(; i < days; i++){
20639             intDay = i - startingPos + 1;
20640             textEls[i].innerHTML = (intDay);
20641             d.setDate(d.getDate()+1);
20642             
20643             cells[i].className = ''; // "x-date-active";
20644             setCellClass(this, cells[i]);
20645         }
20646         var extraDays = 0;
20647         
20648         for(; i < 42; i++) {
20649             textEls[i].innerHTML = (++extraDays);
20650             d.setDate(d.getDate()+1);
20651             
20652             cells[i].className = "fc-future fc-other-month";
20653             setCellClass(this, cells[i]);
20654         }
20655         
20656         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20657         
20658         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20659         
20660         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20661         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20662         
20663         if(totalRows != 6){
20664             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20665             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20666         }
20667         
20668         this.fireEvent('monthchange', this, date);
20669         
20670         
20671         /*
20672         if(!this.internalRender){
20673             var main = this.el.dom.firstChild;
20674             var w = main.offsetWidth;
20675             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20676             Roo.fly(main).setWidth(w);
20677             this.internalRender = true;
20678             // opera does not respect the auto grow header center column
20679             // then, after it gets a width opera refuses to recalculate
20680             // without a second pass
20681             if(Roo.isOpera && !this.secondPass){
20682                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20683                 this.secondPass = true;
20684                 this.update.defer(10, this, [date]);
20685             }
20686         }
20687         */
20688         
20689     },
20690     
20691     findCell : function(dt) {
20692         dt = dt.clearTime().getTime();
20693         var ret = false;
20694         this.cells.each(function(c){
20695             //Roo.log("check " +c.dateValue + '?=' + dt);
20696             if(c.dateValue == dt){
20697                 ret = c;
20698                 return false;
20699             }
20700             return true;
20701         });
20702         
20703         return ret;
20704     },
20705     
20706     findCells : function(ev) {
20707         var s = ev.start.clone().clearTime().getTime();
20708        // Roo.log(s);
20709         var e= ev.end.clone().clearTime().getTime();
20710        // Roo.log(e);
20711         var ret = [];
20712         this.cells.each(function(c){
20713              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20714             
20715             if(c.dateValue > e){
20716                 return ;
20717             }
20718             if(c.dateValue < s){
20719                 return ;
20720             }
20721             ret.push(c);
20722         });
20723         
20724         return ret;    
20725     },
20726     
20727 //    findBestRow: function(cells)
20728 //    {
20729 //        var ret = 0;
20730 //        
20731 //        for (var i =0 ; i < cells.length;i++) {
20732 //            ret  = Math.max(cells[i].rows || 0,ret);
20733 //        }
20734 //        return ret;
20735 //        
20736 //    },
20737     
20738     
20739     addItem : function(ev)
20740     {
20741         // look for vertical location slot in
20742         var cells = this.findCells(ev);
20743         
20744 //        ev.row = this.findBestRow(cells);
20745         
20746         // work out the location.
20747         
20748         var crow = false;
20749         var rows = [];
20750         for(var i =0; i < cells.length; i++) {
20751             
20752             cells[i].row = cells[0].row;
20753             
20754             if(i == 0){
20755                 cells[i].row = cells[i].row + 1;
20756             }
20757             
20758             if (!crow) {
20759                 crow = {
20760                     start : cells[i],
20761                     end :  cells[i]
20762                 };
20763                 continue;
20764             }
20765             if (crow.start.getY() == cells[i].getY()) {
20766                 // on same row.
20767                 crow.end = cells[i];
20768                 continue;
20769             }
20770             // different row.
20771             rows.push(crow);
20772             crow = {
20773                 start: cells[i],
20774                 end : cells[i]
20775             };
20776             
20777         }
20778         
20779         rows.push(crow);
20780         ev.els = [];
20781         ev.rows = rows;
20782         ev.cells = cells;
20783         
20784         cells[0].events.push(ev);
20785         
20786         this.calevents.push(ev);
20787     },
20788     
20789     clearEvents: function() {
20790         
20791         if(!this.calevents){
20792             return;
20793         }
20794         
20795         Roo.each(this.cells.elements, function(c){
20796             c.row = 0;
20797             c.events = [];
20798             c.more = [];
20799         });
20800         
20801         Roo.each(this.calevents, function(e) {
20802             Roo.each(e.els, function(el) {
20803                 el.un('mouseenter' ,this.onEventEnter, this);
20804                 el.un('mouseleave' ,this.onEventLeave, this);
20805                 el.remove();
20806             },this);
20807         },this);
20808         
20809         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20810             e.remove();
20811         });
20812         
20813     },
20814     
20815     renderEvents: function()
20816     {   
20817         var _this = this;
20818         
20819         this.cells.each(function(c) {
20820             
20821             if(c.row < 5){
20822                 return;
20823             }
20824             
20825             var ev = c.events;
20826             
20827             var r = 4;
20828             if(c.row != c.events.length){
20829                 r = 4 - (4 - (c.row - c.events.length));
20830             }
20831             
20832             c.events = ev.slice(0, r);
20833             c.more = ev.slice(r);
20834             
20835             if(c.more.length && c.more.length == 1){
20836                 c.events.push(c.more.pop());
20837             }
20838             
20839             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20840             
20841         });
20842             
20843         this.cells.each(function(c) {
20844             
20845             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20846             
20847             
20848             for (var e = 0; e < c.events.length; e++){
20849                 var ev = c.events[e];
20850                 var rows = ev.rows;
20851                 
20852                 for(var i = 0; i < rows.length; i++) {
20853                 
20854                     // how many rows should it span..
20855
20856                     var  cfg = {
20857                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20858                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20859
20860                         unselectable : "on",
20861                         cn : [
20862                             {
20863                                 cls: 'fc-event-inner',
20864                                 cn : [
20865     //                                {
20866     //                                  tag:'span',
20867     //                                  cls: 'fc-event-time',
20868     //                                  html : cells.length > 1 ? '' : ev.time
20869     //                                },
20870                                     {
20871                                       tag:'span',
20872                                       cls: 'fc-event-title',
20873                                       html : String.format('{0}', ev.title)
20874                                     }
20875
20876
20877                                 ]
20878                             },
20879                             {
20880                                 cls: 'ui-resizable-handle ui-resizable-e',
20881                                 html : '&nbsp;&nbsp;&nbsp'
20882                             }
20883
20884                         ]
20885                     };
20886
20887                     if (i == 0) {
20888                         cfg.cls += ' fc-event-start';
20889                     }
20890                     if ((i+1) == rows.length) {
20891                         cfg.cls += ' fc-event-end';
20892                     }
20893
20894                     var ctr = _this.el.select('.fc-event-container',true).first();
20895                     var cg = ctr.createChild(cfg);
20896
20897                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20898                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20899
20900                     var r = (c.more.length) ? 1 : 0;
20901                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20902                     cg.setWidth(ebox.right - sbox.x -2);
20903
20904                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20905                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20906                     cg.on('click', _this.onEventClick, _this, ev);
20907
20908                     ev.els.push(cg);
20909                     
20910                 }
20911                 
20912             }
20913             
20914             
20915             if(c.more.length){
20916                 var  cfg = {
20917                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20918                     style : 'position: absolute',
20919                     unselectable : "on",
20920                     cn : [
20921                         {
20922                             cls: 'fc-event-inner',
20923                             cn : [
20924                                 {
20925                                   tag:'span',
20926                                   cls: 'fc-event-title',
20927                                   html : 'More'
20928                                 }
20929
20930
20931                             ]
20932                         },
20933                         {
20934                             cls: 'ui-resizable-handle ui-resizable-e',
20935                             html : '&nbsp;&nbsp;&nbsp'
20936                         }
20937
20938                     ]
20939                 };
20940
20941                 var ctr = _this.el.select('.fc-event-container',true).first();
20942                 var cg = ctr.createChild(cfg);
20943
20944                 var sbox = c.select('.fc-day-content',true).first().getBox();
20945                 var ebox = c.select('.fc-day-content',true).first().getBox();
20946                 //Roo.log(cg);
20947                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20948                 cg.setWidth(ebox.right - sbox.x -2);
20949
20950                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20951                 
20952             }
20953             
20954         });
20955         
20956         
20957         
20958     },
20959     
20960     onEventEnter: function (e, el,event,d) {
20961         this.fireEvent('evententer', this, el, event);
20962     },
20963     
20964     onEventLeave: function (e, el,event,d) {
20965         this.fireEvent('eventleave', this, el, event);
20966     },
20967     
20968     onEventClick: function (e, el,event,d) {
20969         this.fireEvent('eventclick', this, el, event);
20970     },
20971     
20972     onMonthChange: function () {
20973         this.store.load();
20974     },
20975     
20976     onMoreEventClick: function(e, el, more)
20977     {
20978         var _this = this;
20979         
20980         this.calpopover.placement = 'right';
20981         this.calpopover.setTitle('More');
20982         
20983         this.calpopover.setContent('');
20984         
20985         var ctr = this.calpopover.el.select('.popover-content', true).first();
20986         
20987         Roo.each(more, function(m){
20988             var cfg = {
20989                 cls : 'fc-event-hori fc-event-draggable',
20990                 html : m.title
20991             };
20992             var cg = ctr.createChild(cfg);
20993             
20994             cg.on('click', _this.onEventClick, _this, m);
20995         });
20996         
20997         this.calpopover.show(el);
20998         
20999         
21000     },
21001     
21002     onLoad: function () 
21003     {   
21004         this.calevents = [];
21005         var cal = this;
21006         
21007         if(this.store.getCount() > 0){
21008             this.store.data.each(function(d){
21009                cal.addItem({
21010                     id : d.data.id,
21011                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21012                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21013                     time : d.data.start_time,
21014                     title : d.data.title,
21015                     description : d.data.description,
21016                     venue : d.data.venue
21017                 });
21018             });
21019         }
21020         
21021         this.renderEvents();
21022         
21023         if(this.calevents.length && this.loadMask){
21024             this.maskEl.hide();
21025         }
21026     },
21027     
21028     onBeforeLoad: function()
21029     {
21030         this.clearEvents();
21031         if(this.loadMask){
21032             this.maskEl.show();
21033         }
21034     }
21035 });
21036
21037  
21038  /*
21039  * - LGPL
21040  *
21041  * element
21042  * 
21043  */
21044
21045 /**
21046  * @class Roo.bootstrap.Popover
21047  * @extends Roo.bootstrap.Component
21048  * Bootstrap Popover class
21049  * @cfg {String} html contents of the popover   (or false to use children..)
21050  * @cfg {String} title of popover (or false to hide)
21051  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21052  * @cfg {String} trigger click || hover (or false to trigger manually)
21053  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21054  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21055  *      - if false and it has a 'parent' then it will be automatically added to that element
21056  *      - if string - Roo.get  will be called 
21057  * @cfg {Number} delay - delay before showing
21058  
21059  * @constructor
21060  * Create a new Popover
21061  * @param {Object} config The config object
21062  */
21063
21064 Roo.bootstrap.Popover = function(config){
21065     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21066     
21067     this.addEvents({
21068         // raw events
21069          /**
21070          * @event show
21071          * After the popover show
21072          * 
21073          * @param {Roo.bootstrap.Popover} this
21074          */
21075         "show" : true,
21076         /**
21077          * @event hide
21078          * After the popover hide
21079          * 
21080          * @param {Roo.bootstrap.Popover} this
21081          */
21082         "hide" : true
21083     });
21084 };
21085
21086 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21087     
21088     title: false,
21089     html: false,
21090     
21091     placement : 'right',
21092     trigger : 'hover', // hover
21093     modal : false,
21094     delay : 0,
21095     
21096     over: false,
21097     
21098     can_build_overlaid : false,
21099     
21100     maskEl : false, // the mask element
21101     headerEl : false,
21102     contentEl : false,
21103     alignEl : false, // when show is called with an element - this get's stored.
21104     
21105     getChildContainer : function()
21106     {
21107         return this.contentEl;
21108         
21109     },
21110     getPopoverHeader : function()
21111     {
21112         this.title = true; // flag not to hide it..
21113         this.headerEl.addClass('p-0');
21114         return this.headerEl
21115     },
21116     
21117     
21118     getAutoCreate : function(){
21119          
21120         var cfg = {
21121            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21122            style: 'display:block',
21123            cn : [
21124                 {
21125                     cls : 'arrow'
21126                 },
21127                 {
21128                     cls : 'popover-inner ',
21129                     cn : [
21130                         {
21131                             tag: 'h3',
21132                             cls: 'popover-title popover-header',
21133                             html : this.title === false ? '' : this.title
21134                         },
21135                         {
21136                             cls : 'popover-content popover-body '  + (this.cls || ''),
21137                             html : this.html || ''
21138                         }
21139                     ]
21140                     
21141                 }
21142            ]
21143         };
21144         
21145         return cfg;
21146     },
21147     /**
21148      * @param {string} the title
21149      */
21150     setTitle: function(str)
21151     {
21152         this.title = str;
21153         if (this.el) {
21154             this.headerEl.dom.innerHTML = str;
21155         }
21156         
21157     },
21158     /**
21159      * @param {string} the body content
21160      */
21161     setContent: function(str)
21162     {
21163         this.html = str;
21164         if (this.contentEl) {
21165             this.contentEl.dom.innerHTML = str;
21166         }
21167         
21168     },
21169     // as it get's added to the bottom of the page.
21170     onRender : function(ct, position)
21171     {
21172         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21173         
21174         
21175         
21176         if(!this.el){
21177             var cfg = Roo.apply({},  this.getAutoCreate());
21178             cfg.id = Roo.id();
21179             
21180             if (this.cls) {
21181                 cfg.cls += ' ' + this.cls;
21182             }
21183             if (this.style) {
21184                 cfg.style = this.style;
21185             }
21186             //Roo.log("adding to ");
21187             this.el = Roo.get(document.body).createChild(cfg, position);
21188 //            Roo.log(this.el);
21189         }
21190         
21191         this.contentEl = this.el.select('.popover-content',true).first();
21192         this.headerEl =  this.el.select('.popover-title',true).first();
21193         
21194         var nitems = [];
21195         if(typeof(this.items) != 'undefined'){
21196             var items = this.items;
21197             delete this.items;
21198
21199             for(var i =0;i < items.length;i++) {
21200                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21201             }
21202         }
21203
21204         this.items = nitems;
21205         
21206         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21207         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21208         
21209         
21210         
21211         this.initEvents();
21212     },
21213     
21214     resizeMask : function()
21215     {
21216         this.maskEl.setSize(
21217             Roo.lib.Dom.getViewWidth(true),
21218             Roo.lib.Dom.getViewHeight(true)
21219         );
21220     },
21221     
21222     initEvents : function()
21223     {
21224         
21225         if (!this.modal) { 
21226             Roo.bootstrap.Popover.register(this);
21227         }
21228          
21229         this.arrowEl = this.el.select('.arrow',true).first();
21230         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21231         this.el.enableDisplayMode('block');
21232         this.el.hide();
21233  
21234         
21235         if (this.over === false && !this.parent()) {
21236             return; 
21237         }
21238         if (this.triggers === false) {
21239             return;
21240         }
21241          
21242         // support parent
21243         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21244         var triggers = this.trigger ? this.trigger.split(' ') : [];
21245         Roo.each(triggers, function(trigger) {
21246         
21247             if (trigger == 'click') {
21248                 on_el.on('click', this.toggle, this);
21249             } else if (trigger != 'manual') {
21250                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21251                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21252       
21253                 on_el.on(eventIn  ,this.enter, this);
21254                 on_el.on(eventOut, this.leave, this);
21255             }
21256         }, this);
21257     },
21258     
21259     
21260     // private
21261     timeout : null,
21262     hoverState : null,
21263     
21264     toggle : function () {
21265         this.hoverState == 'in' ? this.leave() : this.enter();
21266     },
21267     
21268     enter : function () {
21269         
21270         clearTimeout(this.timeout);
21271     
21272         this.hoverState = 'in';
21273     
21274         if (!this.delay || !this.delay.show) {
21275             this.show();
21276             return;
21277         }
21278         var _t = this;
21279         this.timeout = setTimeout(function () {
21280             if (_t.hoverState == 'in') {
21281                 _t.show();
21282             }
21283         }, this.delay.show)
21284     },
21285     
21286     leave : function() {
21287         clearTimeout(this.timeout);
21288     
21289         this.hoverState = 'out';
21290     
21291         if (!this.delay || !this.delay.hide) {
21292             this.hide();
21293             return;
21294         }
21295         var _t = this;
21296         this.timeout = setTimeout(function () {
21297             if (_t.hoverState == 'out') {
21298                 _t.hide();
21299             }
21300         }, this.delay.hide)
21301     },
21302     /**
21303      * Show the popover
21304      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21305      * @param {string} (left|right|top|bottom) position
21306      */
21307     show : function (on_el, placement)
21308     {
21309         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21310         on_el = on_el || false; // default to false
21311          
21312         if (!on_el) {
21313             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21314                 on_el = this.parent().el;
21315             } else if (this.over) {
21316                 on_el = Roo.get(this.over);
21317             }
21318             
21319         }
21320         
21321         this.alignEl = Roo.get( on_el );
21322
21323         if (!this.el) {
21324             this.render(document.body);
21325         }
21326         
21327         
21328          
21329         
21330         if (this.title === false) {
21331             this.headerEl.hide();
21332         }
21333         
21334        
21335         this.el.show();
21336         this.el.dom.style.display = 'block';
21337          
21338  
21339         if (this.alignEl) {
21340             this.updatePosition(this.placement, true);
21341              
21342         } else {
21343             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21344             var es = this.el.getSize();
21345             var x = Roo.lib.Dom.getViewWidth()/2;
21346             var y = Roo.lib.Dom.getViewHeight()/2;
21347             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21348             
21349         }
21350
21351         
21352         //var arrow = this.el.select('.arrow',true).first();
21353         //arrow.set(align[2], 
21354         
21355         this.el.addClass('in');
21356         
21357          
21358         
21359         this.hoverState = 'in';
21360         
21361         if (this.modal) {
21362             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21363             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21364             this.maskEl.dom.style.display = 'block';
21365             this.maskEl.addClass('show');
21366         }
21367         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21368  
21369         this.fireEvent('show', this);
21370         
21371     },
21372     /**
21373      * fire this manually after loading a grid in the table for example
21374      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21375      * @param {Boolean} try and move it if we cant get right position.
21376      */
21377     updatePosition : function(placement, try_move)
21378     {
21379         // allow for calling with no parameters
21380         placement = placement   ? placement :  this.placement;
21381         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21382         
21383         this.el.removeClass([
21384             'fade','top','bottom', 'left', 'right','in',
21385             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21386         ]);
21387         this.el.addClass(placement + ' bs-popover-' + placement);
21388         
21389         if (!this.alignEl ) {
21390             return false;
21391         }
21392         
21393         switch (placement) {
21394             case 'right':
21395                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21396                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21397                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21398                     //normal display... or moved up/down.
21399                     this.el.setXY(offset);
21400                     var xy = this.alignEl.getAnchorXY('tr', false);
21401                     xy[0]+=2;xy[1]+=5;
21402                     this.arrowEl.setXY(xy);
21403                     return true;
21404                 }
21405                 // continue through...
21406                 return this.updatePosition('left', false);
21407                 
21408             
21409             case 'left':
21410                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21411                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21412                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21413                     //normal display... or moved up/down.
21414                     this.el.setXY(offset);
21415                     var xy = this.alignEl.getAnchorXY('tl', false);
21416                     xy[0]-=10;xy[1]+=5; // << fix me
21417                     this.arrowEl.setXY(xy);
21418                     return true;
21419                 }
21420                 // call self...
21421                 return this.updatePosition('right', false);
21422             
21423             case 'top':
21424                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21425                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21426                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21427                     //normal display... or moved up/down.
21428                     this.el.setXY(offset);
21429                     var xy = this.alignEl.getAnchorXY('t', false);
21430                     xy[1]-=10; // << fix me
21431                     this.arrowEl.setXY(xy);
21432                     return true;
21433                 }
21434                 // fall through
21435                return this.updatePosition('bottom', false);
21436             
21437             case 'bottom':
21438                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21439                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21440                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21441                     //normal display... or moved up/down.
21442                     this.el.setXY(offset);
21443                     var xy = this.alignEl.getAnchorXY('b', false);
21444                      xy[1]+=2; // << fix me
21445                     this.arrowEl.setXY(xy);
21446                     return true;
21447                 }
21448                 // fall through
21449                 return this.updatePosition('top', false);
21450                 
21451             
21452         }
21453         
21454         
21455         return false;
21456     },
21457     
21458     hide : function()
21459     {
21460         this.el.setXY([0,0]);
21461         this.el.removeClass('in');
21462         this.el.hide();
21463         this.hoverState = null;
21464         this.maskEl.hide(); // always..
21465         this.fireEvent('hide', this);
21466     }
21467     
21468 });
21469
21470
21471 Roo.apply(Roo.bootstrap.Popover, {
21472
21473     alignment : {
21474         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21475         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21476         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21477         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21478     },
21479     
21480     zIndex : 20001,
21481
21482     clickHander : false,
21483     
21484     
21485
21486     onMouseDown : function(e)
21487     {
21488         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21489             /// what is nothing is showing..
21490             this.hideAll();
21491         }
21492          
21493     },
21494     
21495     
21496     popups : [],
21497     
21498     register : function(popup)
21499     {
21500         if (!Roo.bootstrap.Popover.clickHandler) {
21501             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21502         }
21503         // hide other popups.
21504         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21505         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21506         this.hideAll(); //<< why?
21507         //this.popups.push(popup);
21508     },
21509     hideAll : function()
21510     {
21511         this.popups.forEach(function(p) {
21512             p.hide();
21513         });
21514     },
21515     onShow : function() {
21516         Roo.bootstrap.Popover.popups.push(this);
21517     },
21518     onHide : function() {
21519         Roo.bootstrap.Popover.popups.remove(this);
21520     } 
21521
21522 });/*
21523  * - LGPL
21524  *
21525  * Card header - holder for the card header elements.
21526  * 
21527  */
21528
21529 /**
21530  * @class Roo.bootstrap.PopoverNav
21531  * @extends Roo.bootstrap.NavGroup
21532  * Bootstrap Popover header navigation class
21533  * @constructor
21534  * Create a new Popover Header Navigation 
21535  * @param {Object} config The config object
21536  */
21537
21538 Roo.bootstrap.PopoverNav = function(config){
21539     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21540 };
21541
21542 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21543     
21544     
21545     container_method : 'getPopoverHeader' 
21546     
21547      
21548     
21549     
21550    
21551 });
21552
21553  
21554
21555  /*
21556  * - LGPL
21557  *
21558  * Progress
21559  * 
21560  */
21561
21562 /**
21563  * @class Roo.bootstrap.Progress
21564  * @extends Roo.bootstrap.Component
21565  * Bootstrap Progress class
21566  * @cfg {Boolean} striped striped of the progress bar
21567  * @cfg {Boolean} active animated of the progress bar
21568  * 
21569  * 
21570  * @constructor
21571  * Create a new Progress
21572  * @param {Object} config The config object
21573  */
21574
21575 Roo.bootstrap.Progress = function(config){
21576     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21577 };
21578
21579 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21580     
21581     striped : false,
21582     active: false,
21583     
21584     getAutoCreate : function(){
21585         var cfg = {
21586             tag: 'div',
21587             cls: 'progress'
21588         };
21589         
21590         
21591         if(this.striped){
21592             cfg.cls += ' progress-striped';
21593         }
21594       
21595         if(this.active){
21596             cfg.cls += ' active';
21597         }
21598         
21599         
21600         return cfg;
21601     }
21602    
21603 });
21604
21605  
21606
21607  /*
21608  * - LGPL
21609  *
21610  * ProgressBar
21611  * 
21612  */
21613
21614 /**
21615  * @class Roo.bootstrap.ProgressBar
21616  * @extends Roo.bootstrap.Component
21617  * Bootstrap ProgressBar class
21618  * @cfg {Number} aria_valuenow aria-value now
21619  * @cfg {Number} aria_valuemin aria-value min
21620  * @cfg {Number} aria_valuemax aria-value max
21621  * @cfg {String} label label for the progress bar
21622  * @cfg {String} panel (success | info | warning | danger )
21623  * @cfg {String} role role of the progress bar
21624  * @cfg {String} sr_only text
21625  * 
21626  * 
21627  * @constructor
21628  * Create a new ProgressBar
21629  * @param {Object} config The config object
21630  */
21631
21632 Roo.bootstrap.ProgressBar = function(config){
21633     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21634 };
21635
21636 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21637     
21638     aria_valuenow : 0,
21639     aria_valuemin : 0,
21640     aria_valuemax : 100,
21641     label : false,
21642     panel : false,
21643     role : false,
21644     sr_only: false,
21645     
21646     getAutoCreate : function()
21647     {
21648         
21649         var cfg = {
21650             tag: 'div',
21651             cls: 'progress-bar',
21652             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21653         };
21654         
21655         if(this.sr_only){
21656             cfg.cn = {
21657                 tag: 'span',
21658                 cls: 'sr-only',
21659                 html: this.sr_only
21660             }
21661         }
21662         
21663         if(this.role){
21664             cfg.role = this.role;
21665         }
21666         
21667         if(this.aria_valuenow){
21668             cfg['aria-valuenow'] = this.aria_valuenow;
21669         }
21670         
21671         if(this.aria_valuemin){
21672             cfg['aria-valuemin'] = this.aria_valuemin;
21673         }
21674         
21675         if(this.aria_valuemax){
21676             cfg['aria-valuemax'] = this.aria_valuemax;
21677         }
21678         
21679         if(this.label && !this.sr_only){
21680             cfg.html = this.label;
21681         }
21682         
21683         if(this.panel){
21684             cfg.cls += ' progress-bar-' + this.panel;
21685         }
21686         
21687         return cfg;
21688     },
21689     
21690     update : function(aria_valuenow)
21691     {
21692         this.aria_valuenow = aria_valuenow;
21693         
21694         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21695     }
21696    
21697 });
21698
21699  
21700
21701  /*
21702  * - LGPL
21703  *
21704  * column
21705  * 
21706  */
21707
21708 /**
21709  * @class Roo.bootstrap.TabGroup
21710  * @extends Roo.bootstrap.Column
21711  * Bootstrap Column class
21712  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21713  * @cfg {Boolean} carousel true to make the group behave like a carousel
21714  * @cfg {Boolean} bullets show bullets for the panels
21715  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21716  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21717  * @cfg {Boolean} showarrow (true|false) show arrow default true
21718  * 
21719  * @constructor
21720  * Create a new TabGroup
21721  * @param {Object} config The config object
21722  */
21723
21724 Roo.bootstrap.TabGroup = function(config){
21725     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21726     if (!this.navId) {
21727         this.navId = Roo.id();
21728     }
21729     this.tabs = [];
21730     Roo.bootstrap.TabGroup.register(this);
21731     
21732 };
21733
21734 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21735     
21736     carousel : false,
21737     transition : false,
21738     bullets : 0,
21739     timer : 0,
21740     autoslide : false,
21741     slideFn : false,
21742     slideOnTouch : false,
21743     showarrow : true,
21744     
21745     getAutoCreate : function()
21746     {
21747         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21748         
21749         cfg.cls += ' tab-content';
21750         
21751         if (this.carousel) {
21752             cfg.cls += ' carousel slide';
21753             
21754             cfg.cn = [{
21755                cls : 'carousel-inner',
21756                cn : []
21757             }];
21758         
21759             if(this.bullets  && !Roo.isTouch){
21760                 
21761                 var bullets = {
21762                     cls : 'carousel-bullets',
21763                     cn : []
21764                 };
21765                
21766                 if(this.bullets_cls){
21767                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21768                 }
21769                 
21770                 bullets.cn.push({
21771                     cls : 'clear'
21772                 });
21773                 
21774                 cfg.cn[0].cn.push(bullets);
21775             }
21776             
21777             if(this.showarrow){
21778                 cfg.cn[0].cn.push({
21779                     tag : 'div',
21780                     class : 'carousel-arrow',
21781                     cn : [
21782                         {
21783                             tag : 'div',
21784                             class : 'carousel-prev',
21785                             cn : [
21786                                 {
21787                                     tag : 'i',
21788                                     class : 'fa fa-chevron-left'
21789                                 }
21790                             ]
21791                         },
21792                         {
21793                             tag : 'div',
21794                             class : 'carousel-next',
21795                             cn : [
21796                                 {
21797                                     tag : 'i',
21798                                     class : 'fa fa-chevron-right'
21799                                 }
21800                             ]
21801                         }
21802                     ]
21803                 });
21804             }
21805             
21806         }
21807         
21808         return cfg;
21809     },
21810     
21811     initEvents:  function()
21812     {
21813 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21814 //            this.el.on("touchstart", this.onTouchStart, this);
21815 //        }
21816         
21817         if(this.autoslide){
21818             var _this = this;
21819             
21820             this.slideFn = window.setInterval(function() {
21821                 _this.showPanelNext();
21822             }, this.timer);
21823         }
21824         
21825         if(this.showarrow){
21826             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21827             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21828         }
21829         
21830         
21831     },
21832     
21833 //    onTouchStart : function(e, el, o)
21834 //    {
21835 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21836 //            return;
21837 //        }
21838 //        
21839 //        this.showPanelNext();
21840 //    },
21841     
21842     
21843     getChildContainer : function()
21844     {
21845         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21846     },
21847     
21848     /**
21849     * register a Navigation item
21850     * @param {Roo.bootstrap.NavItem} the navitem to add
21851     */
21852     register : function(item)
21853     {
21854         this.tabs.push( item);
21855         item.navId = this.navId; // not really needed..
21856         this.addBullet();
21857     
21858     },
21859     
21860     getActivePanel : function()
21861     {
21862         var r = false;
21863         Roo.each(this.tabs, function(t) {
21864             if (t.active) {
21865                 r = t;
21866                 return false;
21867             }
21868             return null;
21869         });
21870         return r;
21871         
21872     },
21873     getPanelByName : function(n)
21874     {
21875         var r = false;
21876         Roo.each(this.tabs, function(t) {
21877             if (t.tabId == n) {
21878                 r = t;
21879                 return false;
21880             }
21881             return null;
21882         });
21883         return r;
21884     },
21885     indexOfPanel : function(p)
21886     {
21887         var r = false;
21888         Roo.each(this.tabs, function(t,i) {
21889             if (t.tabId == p.tabId) {
21890                 r = i;
21891                 return false;
21892             }
21893             return null;
21894         });
21895         return r;
21896     },
21897     /**
21898      * show a specific panel
21899      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21900      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21901      */
21902     showPanel : function (pan)
21903     {
21904         if(this.transition || typeof(pan) == 'undefined'){
21905             Roo.log("waiting for the transitionend");
21906             return false;
21907         }
21908         
21909         if (typeof(pan) == 'number') {
21910             pan = this.tabs[pan];
21911         }
21912         
21913         if (typeof(pan) == 'string') {
21914             pan = this.getPanelByName(pan);
21915         }
21916         
21917         var cur = this.getActivePanel();
21918         
21919         if(!pan || !cur){
21920             Roo.log('pan or acitve pan is undefined');
21921             return false;
21922         }
21923         
21924         if (pan.tabId == this.getActivePanel().tabId) {
21925             return true;
21926         }
21927         
21928         if (false === cur.fireEvent('beforedeactivate')) {
21929             return false;
21930         }
21931         
21932         if(this.bullets > 0 && !Roo.isTouch){
21933             this.setActiveBullet(this.indexOfPanel(pan));
21934         }
21935         
21936         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21937             
21938             //class="carousel-item carousel-item-next carousel-item-left"
21939             
21940             this.transition = true;
21941             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21942             var lr = dir == 'next' ? 'left' : 'right';
21943             pan.el.addClass(dir); // or prev
21944             pan.el.addClass('carousel-item-' + dir); // or prev
21945             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21946             cur.el.addClass(lr); // or right
21947             pan.el.addClass(lr);
21948             cur.el.addClass('carousel-item-' +lr); // or right
21949             pan.el.addClass('carousel-item-' +lr);
21950             
21951             
21952             var _this = this;
21953             cur.el.on('transitionend', function() {
21954                 Roo.log("trans end?");
21955                 
21956                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21957                 pan.setActive(true);
21958                 
21959                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21960                 cur.setActive(false);
21961                 
21962                 _this.transition = false;
21963                 
21964             }, this, { single:  true } );
21965             
21966             return true;
21967         }
21968         
21969         cur.setActive(false);
21970         pan.setActive(true);
21971         
21972         return true;
21973         
21974     },
21975     showPanelNext : function()
21976     {
21977         var i = this.indexOfPanel(this.getActivePanel());
21978         
21979         if (i >= this.tabs.length - 1 && !this.autoslide) {
21980             return;
21981         }
21982         
21983         if (i >= this.tabs.length - 1 && this.autoslide) {
21984             i = -1;
21985         }
21986         
21987         this.showPanel(this.tabs[i+1]);
21988     },
21989     
21990     showPanelPrev : function()
21991     {
21992         var i = this.indexOfPanel(this.getActivePanel());
21993         
21994         if (i  < 1 && !this.autoslide) {
21995             return;
21996         }
21997         
21998         if (i < 1 && this.autoslide) {
21999             i = this.tabs.length;
22000         }
22001         
22002         this.showPanel(this.tabs[i-1]);
22003     },
22004     
22005     
22006     addBullet: function()
22007     {
22008         if(!this.bullets || Roo.isTouch){
22009             return;
22010         }
22011         var ctr = this.el.select('.carousel-bullets',true).first();
22012         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22013         var bullet = ctr.createChild({
22014             cls : 'bullet bullet-' + i
22015         },ctr.dom.lastChild);
22016         
22017         
22018         var _this = this;
22019         
22020         bullet.on('click', (function(e, el, o, ii, t){
22021
22022             e.preventDefault();
22023
22024             this.showPanel(ii);
22025
22026             if(this.autoslide && this.slideFn){
22027                 clearInterval(this.slideFn);
22028                 this.slideFn = window.setInterval(function() {
22029                     _this.showPanelNext();
22030                 }, this.timer);
22031             }
22032
22033         }).createDelegate(this, [i, bullet], true));
22034                 
22035         
22036     },
22037      
22038     setActiveBullet : function(i)
22039     {
22040         if(Roo.isTouch){
22041             return;
22042         }
22043         
22044         Roo.each(this.el.select('.bullet', true).elements, function(el){
22045             el.removeClass('selected');
22046         });
22047
22048         var bullet = this.el.select('.bullet-' + i, true).first();
22049         
22050         if(!bullet){
22051             return;
22052         }
22053         
22054         bullet.addClass('selected');
22055     }
22056     
22057     
22058   
22059 });
22060
22061  
22062
22063  
22064  
22065 Roo.apply(Roo.bootstrap.TabGroup, {
22066     
22067     groups: {},
22068      /**
22069     * register a Navigation Group
22070     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22071     */
22072     register : function(navgrp)
22073     {
22074         this.groups[navgrp.navId] = navgrp;
22075         
22076     },
22077     /**
22078     * fetch a Navigation Group based on the navigation ID
22079     * if one does not exist , it will get created.
22080     * @param {string} the navgroup to add
22081     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22082     */
22083     get: function(navId) {
22084         if (typeof(this.groups[navId]) == 'undefined') {
22085             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22086         }
22087         return this.groups[navId] ;
22088     }
22089     
22090     
22091     
22092 });
22093
22094  /*
22095  * - LGPL
22096  *
22097  * TabPanel
22098  * 
22099  */
22100
22101 /**
22102  * @class Roo.bootstrap.TabPanel
22103  * @extends Roo.bootstrap.Component
22104  * Bootstrap TabPanel class
22105  * @cfg {Boolean} active panel active
22106  * @cfg {String} html panel content
22107  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22108  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22109  * @cfg {String} href click to link..
22110  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22111  * 
22112  * 
22113  * @constructor
22114  * Create a new TabPanel
22115  * @param {Object} config The config object
22116  */
22117
22118 Roo.bootstrap.TabPanel = function(config){
22119     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22120     this.addEvents({
22121         /**
22122              * @event changed
22123              * Fires when the active status changes
22124              * @param {Roo.bootstrap.TabPanel} this
22125              * @param {Boolean} state the new state
22126             
22127          */
22128         'changed': true,
22129         /**
22130              * @event beforedeactivate
22131              * Fires before a tab is de-activated - can be used to do validation on a form.
22132              * @param {Roo.bootstrap.TabPanel} this
22133              * @return {Boolean} false if there is an error
22134             
22135          */
22136         'beforedeactivate': true
22137      });
22138     
22139     this.tabId = this.tabId || Roo.id();
22140   
22141 };
22142
22143 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22144     
22145     active: false,
22146     html: false,
22147     tabId: false,
22148     navId : false,
22149     href : '',
22150     touchSlide : false,
22151     getAutoCreate : function(){
22152         
22153         
22154         var cfg = {
22155             tag: 'div',
22156             // item is needed for carousel - not sure if it has any effect otherwise
22157             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22158             html: this.html || ''
22159         };
22160         
22161         if(this.active){
22162             cfg.cls += ' active';
22163         }
22164         
22165         if(this.tabId){
22166             cfg.tabId = this.tabId;
22167         }
22168         
22169         
22170         
22171         return cfg;
22172     },
22173     
22174     initEvents:  function()
22175     {
22176         var p = this.parent();
22177         
22178         this.navId = this.navId || p.navId;
22179         
22180         if (typeof(this.navId) != 'undefined') {
22181             // not really needed.. but just in case.. parent should be a NavGroup.
22182             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22183             
22184             tg.register(this);
22185             
22186             var i = tg.tabs.length - 1;
22187             
22188             if(this.active && tg.bullets > 0 && i < tg.bullets){
22189                 tg.setActiveBullet(i);
22190             }
22191         }
22192         
22193         this.el.on('click', this.onClick, this);
22194         
22195         if(Roo.isTouch && this.touchSlide){
22196             this.el.on("touchstart", this.onTouchStart, this);
22197             this.el.on("touchmove", this.onTouchMove, this);
22198             this.el.on("touchend", this.onTouchEnd, this);
22199         }
22200         
22201     },
22202     
22203     onRender : function(ct, position)
22204     {
22205         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22206     },
22207     
22208     setActive : function(state)
22209     {
22210         Roo.log("panel - set active " + this.tabId + "=" + state);
22211         
22212         this.active = state;
22213         if (!state) {
22214             this.el.removeClass('active');
22215             
22216         } else  if (!this.el.hasClass('active')) {
22217             this.el.addClass('active');
22218         }
22219         
22220         this.fireEvent('changed', this, state);
22221     },
22222     
22223     onClick : function(e)
22224     {
22225         e.preventDefault();
22226         
22227         if(!this.href.length){
22228             return;
22229         }
22230         
22231         window.location.href = this.href;
22232     },
22233     
22234     startX : 0,
22235     startY : 0,
22236     endX : 0,
22237     endY : 0,
22238     swiping : false,
22239     
22240     onTouchStart : function(e)
22241     {
22242         this.swiping = false;
22243         
22244         this.startX = e.browserEvent.touches[0].clientX;
22245         this.startY = e.browserEvent.touches[0].clientY;
22246     },
22247     
22248     onTouchMove : function(e)
22249     {
22250         this.swiping = true;
22251         
22252         this.endX = e.browserEvent.touches[0].clientX;
22253         this.endY = e.browserEvent.touches[0].clientY;
22254     },
22255     
22256     onTouchEnd : function(e)
22257     {
22258         if(!this.swiping){
22259             this.onClick(e);
22260             return;
22261         }
22262         
22263         var tabGroup = this.parent();
22264         
22265         if(this.endX > this.startX){ // swiping right
22266             tabGroup.showPanelPrev();
22267             return;
22268         }
22269         
22270         if(this.startX > this.endX){ // swiping left
22271             tabGroup.showPanelNext();
22272             return;
22273         }
22274     }
22275     
22276     
22277 });
22278  
22279
22280  
22281
22282  /*
22283  * - LGPL
22284  *
22285  * DateField
22286  * 
22287  */
22288
22289 /**
22290  * @class Roo.bootstrap.DateField
22291  * @extends Roo.bootstrap.Input
22292  * Bootstrap DateField class
22293  * @cfg {Number} weekStart default 0
22294  * @cfg {String} viewMode default empty, (months|years)
22295  * @cfg {String} minViewMode default empty, (months|years)
22296  * @cfg {Number} startDate default -Infinity
22297  * @cfg {Number} endDate default Infinity
22298  * @cfg {Boolean} todayHighlight default false
22299  * @cfg {Boolean} todayBtn default false
22300  * @cfg {Boolean} calendarWeeks default false
22301  * @cfg {Object} daysOfWeekDisabled default empty
22302  * @cfg {Boolean} singleMode default false (true | false)
22303  * 
22304  * @cfg {Boolean} keyboardNavigation default true
22305  * @cfg {String} language default en
22306  * 
22307  * @constructor
22308  * Create a new DateField
22309  * @param {Object} config The config object
22310  */
22311
22312 Roo.bootstrap.DateField = function(config){
22313     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22314      this.addEvents({
22315             /**
22316              * @event show
22317              * Fires when this field show.
22318              * @param {Roo.bootstrap.DateField} this
22319              * @param {Mixed} date The date value
22320              */
22321             show : true,
22322             /**
22323              * @event show
22324              * Fires when this field hide.
22325              * @param {Roo.bootstrap.DateField} this
22326              * @param {Mixed} date The date value
22327              */
22328             hide : true,
22329             /**
22330              * @event select
22331              * Fires when select a date.
22332              * @param {Roo.bootstrap.DateField} this
22333              * @param {Mixed} date The date value
22334              */
22335             select : true,
22336             /**
22337              * @event beforeselect
22338              * Fires when before select a date.
22339              * @param {Roo.bootstrap.DateField} this
22340              * @param {Mixed} date The date value
22341              */
22342             beforeselect : true
22343         });
22344 };
22345
22346 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22347     
22348     /**
22349      * @cfg {String} format
22350      * The default date format string which can be overriden for localization support.  The format must be
22351      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22352      */
22353     format : "m/d/y",
22354     /**
22355      * @cfg {String} altFormats
22356      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22357      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22358      */
22359     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22360     
22361     weekStart : 0,
22362     
22363     viewMode : '',
22364     
22365     minViewMode : '',
22366     
22367     todayHighlight : false,
22368     
22369     todayBtn: false,
22370     
22371     language: 'en',
22372     
22373     keyboardNavigation: true,
22374     
22375     calendarWeeks: false,
22376     
22377     startDate: -Infinity,
22378     
22379     endDate: Infinity,
22380     
22381     daysOfWeekDisabled: [],
22382     
22383     _events: [],
22384     
22385     singleMode : false,
22386     
22387     UTCDate: function()
22388     {
22389         return new Date(Date.UTC.apply(Date, arguments));
22390     },
22391     
22392     UTCToday: function()
22393     {
22394         var today = new Date();
22395         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22396     },
22397     
22398     getDate: function() {
22399             var d = this.getUTCDate();
22400             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22401     },
22402     
22403     getUTCDate: function() {
22404             return this.date;
22405     },
22406     
22407     setDate: function(d) {
22408             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22409     },
22410     
22411     setUTCDate: function(d) {
22412             this.date = d;
22413             this.setValue(this.formatDate(this.date));
22414     },
22415         
22416     onRender: function(ct, position)
22417     {
22418         
22419         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22420         
22421         this.language = this.language || 'en';
22422         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22423         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22424         
22425         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22426         this.format = this.format || 'm/d/y';
22427         this.isInline = false;
22428         this.isInput = true;
22429         this.component = this.el.select('.add-on', true).first() || false;
22430         this.component = (this.component && this.component.length === 0) ? false : this.component;
22431         this.hasInput = this.component && this.inputEl().length;
22432         
22433         if (typeof(this.minViewMode === 'string')) {
22434             switch (this.minViewMode) {
22435                 case 'months':
22436                     this.minViewMode = 1;
22437                     break;
22438                 case 'years':
22439                     this.minViewMode = 2;
22440                     break;
22441                 default:
22442                     this.minViewMode = 0;
22443                     break;
22444             }
22445         }
22446         
22447         if (typeof(this.viewMode === 'string')) {
22448             switch (this.viewMode) {
22449                 case 'months':
22450                     this.viewMode = 1;
22451                     break;
22452                 case 'years':
22453                     this.viewMode = 2;
22454                     break;
22455                 default:
22456                     this.viewMode = 0;
22457                     break;
22458             }
22459         }
22460                 
22461         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22462         
22463 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22464         
22465         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22466         
22467         this.picker().on('mousedown', this.onMousedown, this);
22468         this.picker().on('click', this.onClick, this);
22469         
22470         this.picker().addClass('datepicker-dropdown');
22471         
22472         this.startViewMode = this.viewMode;
22473         
22474         if(this.singleMode){
22475             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22476                 v.setVisibilityMode(Roo.Element.DISPLAY);
22477                 v.hide();
22478             });
22479             
22480             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22481                 v.setStyle('width', '189px');
22482             });
22483         }
22484         
22485         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22486             if(!this.calendarWeeks){
22487                 v.remove();
22488                 return;
22489             }
22490             
22491             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22492             v.attr('colspan', function(i, val){
22493                 return parseInt(val) + 1;
22494             });
22495         });
22496                         
22497         
22498         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22499         
22500         this.setStartDate(this.startDate);
22501         this.setEndDate(this.endDate);
22502         
22503         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22504         
22505         this.fillDow();
22506         this.fillMonths();
22507         this.update();
22508         this.showMode();
22509         
22510         if(this.isInline) {
22511             this.showPopup();
22512         }
22513     },
22514     
22515     picker : function()
22516     {
22517         return this.pickerEl;
22518 //        return this.el.select('.datepicker', true).first();
22519     },
22520     
22521     fillDow: function()
22522     {
22523         var dowCnt = this.weekStart;
22524         
22525         var dow = {
22526             tag: 'tr',
22527             cn: [
22528                 
22529             ]
22530         };
22531         
22532         if(this.calendarWeeks){
22533             dow.cn.push({
22534                 tag: 'th',
22535                 cls: 'cw',
22536                 html: '&nbsp;'
22537             })
22538         }
22539         
22540         while (dowCnt < this.weekStart + 7) {
22541             dow.cn.push({
22542                 tag: 'th',
22543                 cls: 'dow',
22544                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22545             });
22546         }
22547         
22548         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22549     },
22550     
22551     fillMonths: function()
22552     {    
22553         var i = 0;
22554         var months = this.picker().select('>.datepicker-months td', true).first();
22555         
22556         months.dom.innerHTML = '';
22557         
22558         while (i < 12) {
22559             var month = {
22560                 tag: 'span',
22561                 cls: 'month',
22562                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22563             };
22564             
22565             months.createChild(month);
22566         }
22567         
22568     },
22569     
22570     update: function()
22571     {
22572         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;
22573         
22574         if (this.date < this.startDate) {
22575             this.viewDate = new Date(this.startDate);
22576         } else if (this.date > this.endDate) {
22577             this.viewDate = new Date(this.endDate);
22578         } else {
22579             this.viewDate = new Date(this.date);
22580         }
22581         
22582         this.fill();
22583     },
22584     
22585     fill: function() 
22586     {
22587         var d = new Date(this.viewDate),
22588                 year = d.getUTCFullYear(),
22589                 month = d.getUTCMonth(),
22590                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22591                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22592                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22593                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22594                 currentDate = this.date && this.date.valueOf(),
22595                 today = this.UTCToday();
22596         
22597         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22598         
22599 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22600         
22601 //        this.picker.select('>tfoot th.today').
22602 //                                              .text(dates[this.language].today)
22603 //                                              .toggle(this.todayBtn !== false);
22604     
22605         this.updateNavArrows();
22606         this.fillMonths();
22607                                                 
22608         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22609         
22610         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22611          
22612         prevMonth.setUTCDate(day);
22613         
22614         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22615         
22616         var nextMonth = new Date(prevMonth);
22617         
22618         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22619         
22620         nextMonth = nextMonth.valueOf();
22621         
22622         var fillMonths = false;
22623         
22624         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22625         
22626         while(prevMonth.valueOf() <= nextMonth) {
22627             var clsName = '';
22628             
22629             if (prevMonth.getUTCDay() === this.weekStart) {
22630                 if(fillMonths){
22631                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22632                 }
22633                     
22634                 fillMonths = {
22635                     tag: 'tr',
22636                     cn: []
22637                 };
22638                 
22639                 if(this.calendarWeeks){
22640                     // ISO 8601: First week contains first thursday.
22641                     // ISO also states week starts on Monday, but we can be more abstract here.
22642                     var
22643                     // Start of current week: based on weekstart/current date
22644                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22645                     // Thursday of this week
22646                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22647                     // First Thursday of year, year from thursday
22648                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22649                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22650                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22651                     
22652                     fillMonths.cn.push({
22653                         tag: 'td',
22654                         cls: 'cw',
22655                         html: calWeek
22656                     });
22657                 }
22658             }
22659             
22660             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22661                 clsName += ' old';
22662             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22663                 clsName += ' new';
22664             }
22665             if (this.todayHighlight &&
22666                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22667                 prevMonth.getUTCMonth() == today.getMonth() &&
22668                 prevMonth.getUTCDate() == today.getDate()) {
22669                 clsName += ' today';
22670             }
22671             
22672             if (currentDate && prevMonth.valueOf() === currentDate) {
22673                 clsName += ' active';
22674             }
22675             
22676             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22677                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22678                     clsName += ' disabled';
22679             }
22680             
22681             fillMonths.cn.push({
22682                 tag: 'td',
22683                 cls: 'day ' + clsName,
22684                 html: prevMonth.getDate()
22685             });
22686             
22687             prevMonth.setDate(prevMonth.getDate()+1);
22688         }
22689           
22690         var currentYear = this.date && this.date.getUTCFullYear();
22691         var currentMonth = this.date && this.date.getUTCMonth();
22692         
22693         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22694         
22695         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22696             v.removeClass('active');
22697             
22698             if(currentYear === year && k === currentMonth){
22699                 v.addClass('active');
22700             }
22701             
22702             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22703                 v.addClass('disabled');
22704             }
22705             
22706         });
22707         
22708         
22709         year = parseInt(year/10, 10) * 10;
22710         
22711         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22712         
22713         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22714         
22715         year -= 1;
22716         for (var i = -1; i < 11; i++) {
22717             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22718                 tag: 'span',
22719                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22720                 html: year
22721             });
22722             
22723             year += 1;
22724         }
22725     },
22726     
22727     showMode: function(dir) 
22728     {
22729         if (dir) {
22730             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22731         }
22732         
22733         Roo.each(this.picker().select('>div',true).elements, function(v){
22734             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22735             v.hide();
22736         });
22737         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22738     },
22739     
22740     place: function()
22741     {
22742         if(this.isInline) {
22743             return;
22744         }
22745         
22746         this.picker().removeClass(['bottom', 'top']);
22747         
22748         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22749             /*
22750              * place to the top of element!
22751              *
22752              */
22753             
22754             this.picker().addClass('top');
22755             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22756             
22757             return;
22758         }
22759         
22760         this.picker().addClass('bottom');
22761         
22762         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22763     },
22764     
22765     parseDate : function(value)
22766     {
22767         if(!value || value instanceof Date){
22768             return value;
22769         }
22770         var v = Date.parseDate(value, this.format);
22771         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22772             v = Date.parseDate(value, 'Y-m-d');
22773         }
22774         if(!v && this.altFormats){
22775             if(!this.altFormatsArray){
22776                 this.altFormatsArray = this.altFormats.split("|");
22777             }
22778             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22779                 v = Date.parseDate(value, this.altFormatsArray[i]);
22780             }
22781         }
22782         return v;
22783     },
22784     
22785     formatDate : function(date, fmt)
22786     {   
22787         return (!date || !(date instanceof Date)) ?
22788         date : date.dateFormat(fmt || this.format);
22789     },
22790     
22791     onFocus : function()
22792     {
22793         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22794         this.showPopup();
22795     },
22796     
22797     onBlur : function()
22798     {
22799         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22800         
22801         var d = this.inputEl().getValue();
22802         
22803         this.setValue(d);
22804                 
22805         this.hidePopup();
22806     },
22807     
22808     showPopup : function()
22809     {
22810         this.picker().show();
22811         this.update();
22812         this.place();
22813         
22814         this.fireEvent('showpopup', this, this.date);
22815     },
22816     
22817     hidePopup : function()
22818     {
22819         if(this.isInline) {
22820             return;
22821         }
22822         this.picker().hide();
22823         this.viewMode = this.startViewMode;
22824         this.showMode();
22825         
22826         this.fireEvent('hidepopup', this, this.date);
22827         
22828     },
22829     
22830     onMousedown: function(e)
22831     {
22832         e.stopPropagation();
22833         e.preventDefault();
22834     },
22835     
22836     keyup: function(e)
22837     {
22838         Roo.bootstrap.DateField.superclass.keyup.call(this);
22839         this.update();
22840     },
22841
22842     setValue: function(v)
22843     {
22844         if(this.fireEvent('beforeselect', this, v) !== false){
22845             var d = new Date(this.parseDate(v) ).clearTime();
22846         
22847             if(isNaN(d.getTime())){
22848                 this.date = this.viewDate = '';
22849                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22850                 return;
22851             }
22852
22853             v = this.formatDate(d);
22854
22855             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22856
22857             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22858
22859             this.update();
22860
22861             this.fireEvent('select', this, this.date);
22862         }
22863     },
22864     
22865     getValue: function()
22866     {
22867         return this.formatDate(this.date);
22868     },
22869     
22870     fireKey: function(e)
22871     {
22872         if (!this.picker().isVisible()){
22873             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22874                 this.showPopup();
22875             }
22876             return;
22877         }
22878         
22879         var dateChanged = false,
22880         dir, day, month,
22881         newDate, newViewDate;
22882         
22883         switch(e.keyCode){
22884             case 27: // escape
22885                 this.hidePopup();
22886                 e.preventDefault();
22887                 break;
22888             case 37: // left
22889             case 39: // right
22890                 if (!this.keyboardNavigation) {
22891                     break;
22892                 }
22893                 dir = e.keyCode == 37 ? -1 : 1;
22894                 
22895                 if (e.ctrlKey){
22896                     newDate = this.moveYear(this.date, dir);
22897                     newViewDate = this.moveYear(this.viewDate, dir);
22898                 } else if (e.shiftKey){
22899                     newDate = this.moveMonth(this.date, dir);
22900                     newViewDate = this.moveMonth(this.viewDate, dir);
22901                 } else {
22902                     newDate = new Date(this.date);
22903                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22904                     newViewDate = new Date(this.viewDate);
22905                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22906                 }
22907                 if (this.dateWithinRange(newDate)){
22908                     this.date = newDate;
22909                     this.viewDate = newViewDate;
22910                     this.setValue(this.formatDate(this.date));
22911 //                    this.update();
22912                     e.preventDefault();
22913                     dateChanged = true;
22914                 }
22915                 break;
22916             case 38: // up
22917             case 40: // down
22918                 if (!this.keyboardNavigation) {
22919                     break;
22920                 }
22921                 dir = e.keyCode == 38 ? -1 : 1;
22922                 if (e.ctrlKey){
22923                     newDate = this.moveYear(this.date, dir);
22924                     newViewDate = this.moveYear(this.viewDate, dir);
22925                 } else if (e.shiftKey){
22926                     newDate = this.moveMonth(this.date, dir);
22927                     newViewDate = this.moveMonth(this.viewDate, dir);
22928                 } else {
22929                     newDate = new Date(this.date);
22930                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22931                     newViewDate = new Date(this.viewDate);
22932                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22933                 }
22934                 if (this.dateWithinRange(newDate)){
22935                     this.date = newDate;
22936                     this.viewDate = newViewDate;
22937                     this.setValue(this.formatDate(this.date));
22938 //                    this.update();
22939                     e.preventDefault();
22940                     dateChanged = true;
22941                 }
22942                 break;
22943             case 13: // enter
22944                 this.setValue(this.formatDate(this.date));
22945                 this.hidePopup();
22946                 e.preventDefault();
22947                 break;
22948             case 9: // tab
22949                 this.setValue(this.formatDate(this.date));
22950                 this.hidePopup();
22951                 break;
22952             case 16: // shift
22953             case 17: // ctrl
22954             case 18: // alt
22955                 break;
22956             default :
22957                 this.hidePopup();
22958                 
22959         }
22960     },
22961     
22962     
22963     onClick: function(e) 
22964     {
22965         e.stopPropagation();
22966         e.preventDefault();
22967         
22968         var target = e.getTarget();
22969         
22970         if(target.nodeName.toLowerCase() === 'i'){
22971             target = Roo.get(target).dom.parentNode;
22972         }
22973         
22974         var nodeName = target.nodeName;
22975         var className = target.className;
22976         var html = target.innerHTML;
22977         //Roo.log(nodeName);
22978         
22979         switch(nodeName.toLowerCase()) {
22980             case 'th':
22981                 switch(className) {
22982                     case 'switch':
22983                         this.showMode(1);
22984                         break;
22985                     case 'prev':
22986                     case 'next':
22987                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22988                         switch(this.viewMode){
22989                                 case 0:
22990                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22991                                         break;
22992                                 case 1:
22993                                 case 2:
22994                                         this.viewDate = this.moveYear(this.viewDate, dir);
22995                                         break;
22996                         }
22997                         this.fill();
22998                         break;
22999                     case 'today':
23000                         var date = new Date();
23001                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23002 //                        this.fill()
23003                         this.setValue(this.formatDate(this.date));
23004                         
23005                         this.hidePopup();
23006                         break;
23007                 }
23008                 break;
23009             case 'span':
23010                 if (className.indexOf('disabled') < 0) {
23011                 if (!this.viewDate) {
23012                     this.viewDate = new Date();
23013                 }
23014                 this.viewDate.setUTCDate(1);
23015                     if (className.indexOf('month') > -1) {
23016                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23017                     } else {
23018                         var year = parseInt(html, 10) || 0;
23019                         this.viewDate.setUTCFullYear(year);
23020                         
23021                     }
23022                     
23023                     if(this.singleMode){
23024                         this.setValue(this.formatDate(this.viewDate));
23025                         this.hidePopup();
23026                         return;
23027                     }
23028                     
23029                     this.showMode(-1);
23030                     this.fill();
23031                 }
23032                 break;
23033                 
23034             case 'td':
23035                 //Roo.log(className);
23036                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23037                     var day = parseInt(html, 10) || 1;
23038                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23039                         month = (this.viewDate || new Date()).getUTCMonth();
23040
23041                     if (className.indexOf('old') > -1) {
23042                         if(month === 0 ){
23043                             month = 11;
23044                             year -= 1;
23045                         }else{
23046                             month -= 1;
23047                         }
23048                     } else if (className.indexOf('new') > -1) {
23049                         if (month == 11) {
23050                             month = 0;
23051                             year += 1;
23052                         } else {
23053                             month += 1;
23054                         }
23055                     }
23056                     //Roo.log([year,month,day]);
23057                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23058                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23059 //                    this.fill();
23060                     //Roo.log(this.formatDate(this.date));
23061                     this.setValue(this.formatDate(this.date));
23062                     this.hidePopup();
23063                 }
23064                 break;
23065         }
23066     },
23067     
23068     setStartDate: function(startDate)
23069     {
23070         this.startDate = startDate || -Infinity;
23071         if (this.startDate !== -Infinity) {
23072             this.startDate = this.parseDate(this.startDate);
23073         }
23074         this.update();
23075         this.updateNavArrows();
23076     },
23077
23078     setEndDate: function(endDate)
23079     {
23080         this.endDate = endDate || Infinity;
23081         if (this.endDate !== Infinity) {
23082             this.endDate = this.parseDate(this.endDate);
23083         }
23084         this.update();
23085         this.updateNavArrows();
23086     },
23087     
23088     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23089     {
23090         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23091         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23092             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23093         }
23094         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23095             return parseInt(d, 10);
23096         });
23097         this.update();
23098         this.updateNavArrows();
23099     },
23100     
23101     updateNavArrows: function() 
23102     {
23103         if(this.singleMode){
23104             return;
23105         }
23106         
23107         var d = new Date(this.viewDate),
23108         year = d.getUTCFullYear(),
23109         month = d.getUTCMonth();
23110         
23111         Roo.each(this.picker().select('.prev', true).elements, function(v){
23112             v.show();
23113             switch (this.viewMode) {
23114                 case 0:
23115
23116                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23117                         v.hide();
23118                     }
23119                     break;
23120                 case 1:
23121                 case 2:
23122                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23123                         v.hide();
23124                     }
23125                     break;
23126             }
23127         });
23128         
23129         Roo.each(this.picker().select('.next', true).elements, function(v){
23130             v.show();
23131             switch (this.viewMode) {
23132                 case 0:
23133
23134                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23135                         v.hide();
23136                     }
23137                     break;
23138                 case 1:
23139                 case 2:
23140                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23141                         v.hide();
23142                     }
23143                     break;
23144             }
23145         })
23146     },
23147     
23148     moveMonth: function(date, dir)
23149     {
23150         if (!dir) {
23151             return date;
23152         }
23153         var new_date = new Date(date.valueOf()),
23154         day = new_date.getUTCDate(),
23155         month = new_date.getUTCMonth(),
23156         mag = Math.abs(dir),
23157         new_month, test;
23158         dir = dir > 0 ? 1 : -1;
23159         if (mag == 1){
23160             test = dir == -1
23161             // If going back one month, make sure month is not current month
23162             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23163             ? function(){
23164                 return new_date.getUTCMonth() == month;
23165             }
23166             // If going forward one month, make sure month is as expected
23167             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23168             : function(){
23169                 return new_date.getUTCMonth() != new_month;
23170             };
23171             new_month = month + dir;
23172             new_date.setUTCMonth(new_month);
23173             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23174             if (new_month < 0 || new_month > 11) {
23175                 new_month = (new_month + 12) % 12;
23176             }
23177         } else {
23178             // For magnitudes >1, move one month at a time...
23179             for (var i=0; i<mag; i++) {
23180                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23181                 new_date = this.moveMonth(new_date, dir);
23182             }
23183             // ...then reset the day, keeping it in the new month
23184             new_month = new_date.getUTCMonth();
23185             new_date.setUTCDate(day);
23186             test = function(){
23187                 return new_month != new_date.getUTCMonth();
23188             };
23189         }
23190         // Common date-resetting loop -- if date is beyond end of month, make it
23191         // end of month
23192         while (test()){
23193             new_date.setUTCDate(--day);
23194             new_date.setUTCMonth(new_month);
23195         }
23196         return new_date;
23197     },
23198
23199     moveYear: function(date, dir)
23200     {
23201         return this.moveMonth(date, dir*12);
23202     },
23203
23204     dateWithinRange: function(date)
23205     {
23206         return date >= this.startDate && date <= this.endDate;
23207     },
23208
23209     
23210     remove: function() 
23211     {
23212         this.picker().remove();
23213     },
23214     
23215     validateValue : function(value)
23216     {
23217         if(this.getVisibilityEl().hasClass('hidden')){
23218             return true;
23219         }
23220         
23221         if(value.length < 1)  {
23222             if(this.allowBlank){
23223                 return true;
23224             }
23225             return false;
23226         }
23227         
23228         if(value.length < this.minLength){
23229             return false;
23230         }
23231         if(value.length > this.maxLength){
23232             return false;
23233         }
23234         if(this.vtype){
23235             var vt = Roo.form.VTypes;
23236             if(!vt[this.vtype](value, this)){
23237                 return false;
23238             }
23239         }
23240         if(typeof this.validator == "function"){
23241             var msg = this.validator(value);
23242             if(msg !== true){
23243                 return false;
23244             }
23245         }
23246         
23247         if(this.regex && !this.regex.test(value)){
23248             return false;
23249         }
23250         
23251         if(typeof(this.parseDate(value)) == 'undefined'){
23252             return false;
23253         }
23254         
23255         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23256             return false;
23257         }      
23258         
23259         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23260             return false;
23261         } 
23262         
23263         
23264         return true;
23265     },
23266     
23267     reset : function()
23268     {
23269         this.date = this.viewDate = '';
23270         
23271         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23272     }
23273    
23274 });
23275
23276 Roo.apply(Roo.bootstrap.DateField,  {
23277     
23278     head : {
23279         tag: 'thead',
23280         cn: [
23281         {
23282             tag: 'tr',
23283             cn: [
23284             {
23285                 tag: 'th',
23286                 cls: 'prev',
23287                 html: '<i class="fa fa-arrow-left"/>'
23288             },
23289             {
23290                 tag: 'th',
23291                 cls: 'switch',
23292                 colspan: '5'
23293             },
23294             {
23295                 tag: 'th',
23296                 cls: 'next',
23297                 html: '<i class="fa fa-arrow-right"/>'
23298             }
23299
23300             ]
23301         }
23302         ]
23303     },
23304     
23305     content : {
23306         tag: 'tbody',
23307         cn: [
23308         {
23309             tag: 'tr',
23310             cn: [
23311             {
23312                 tag: 'td',
23313                 colspan: '7'
23314             }
23315             ]
23316         }
23317         ]
23318     },
23319     
23320     footer : {
23321         tag: 'tfoot',
23322         cn: [
23323         {
23324             tag: 'tr',
23325             cn: [
23326             {
23327                 tag: 'th',
23328                 colspan: '7',
23329                 cls: 'today'
23330             }
23331                     
23332             ]
23333         }
23334         ]
23335     },
23336     
23337     dates:{
23338         en: {
23339             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23340             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23341             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23342             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23343             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23344             today: "Today"
23345         }
23346     },
23347     
23348     modes: [
23349     {
23350         clsName: 'days',
23351         navFnc: 'Month',
23352         navStep: 1
23353     },
23354     {
23355         clsName: 'months',
23356         navFnc: 'FullYear',
23357         navStep: 1
23358     },
23359     {
23360         clsName: 'years',
23361         navFnc: 'FullYear',
23362         navStep: 10
23363     }]
23364 });
23365
23366 Roo.apply(Roo.bootstrap.DateField,  {
23367   
23368     template : {
23369         tag: 'div',
23370         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23371         cn: [
23372         {
23373             tag: 'div',
23374             cls: 'datepicker-days',
23375             cn: [
23376             {
23377                 tag: 'table',
23378                 cls: 'table-condensed',
23379                 cn:[
23380                 Roo.bootstrap.DateField.head,
23381                 {
23382                     tag: 'tbody'
23383                 },
23384                 Roo.bootstrap.DateField.footer
23385                 ]
23386             }
23387             ]
23388         },
23389         {
23390             tag: 'div',
23391             cls: 'datepicker-months',
23392             cn: [
23393             {
23394                 tag: 'table',
23395                 cls: 'table-condensed',
23396                 cn:[
23397                 Roo.bootstrap.DateField.head,
23398                 Roo.bootstrap.DateField.content,
23399                 Roo.bootstrap.DateField.footer
23400                 ]
23401             }
23402             ]
23403         },
23404         {
23405             tag: 'div',
23406             cls: 'datepicker-years',
23407             cn: [
23408             {
23409                 tag: 'table',
23410                 cls: 'table-condensed',
23411                 cn:[
23412                 Roo.bootstrap.DateField.head,
23413                 Roo.bootstrap.DateField.content,
23414                 Roo.bootstrap.DateField.footer
23415                 ]
23416             }
23417             ]
23418         }
23419         ]
23420     }
23421 });
23422
23423  
23424
23425  /*
23426  * - LGPL
23427  *
23428  * TimeField
23429  * 
23430  */
23431
23432 /**
23433  * @class Roo.bootstrap.TimeField
23434  * @extends Roo.bootstrap.Input
23435  * Bootstrap DateField class
23436  * 
23437  * 
23438  * @constructor
23439  * Create a new TimeField
23440  * @param {Object} config The config object
23441  */
23442
23443 Roo.bootstrap.TimeField = function(config){
23444     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23445     this.addEvents({
23446             /**
23447              * @event show
23448              * Fires when this field show.
23449              * @param {Roo.bootstrap.DateField} thisthis
23450              * @param {Mixed} date The date value
23451              */
23452             show : true,
23453             /**
23454              * @event show
23455              * Fires when this field hide.
23456              * @param {Roo.bootstrap.DateField} this
23457              * @param {Mixed} date The date value
23458              */
23459             hide : true,
23460             /**
23461              * @event select
23462              * Fires when select a date.
23463              * @param {Roo.bootstrap.DateField} this
23464              * @param {Mixed} date The date value
23465              */
23466             select : true
23467         });
23468 };
23469
23470 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23471     
23472     /**
23473      * @cfg {String} format
23474      * The default time format string which can be overriden for localization support.  The format must be
23475      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23476      */
23477     format : "H:i",
23478
23479     getAutoCreate : function()
23480     {
23481         this.after = '<i class="fa far fa-clock"></i>';
23482         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23483         
23484          
23485     },
23486     onRender: function(ct, position)
23487     {
23488         
23489         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23490                 
23491         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23492         
23493         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23494         
23495         this.pop = this.picker().select('>.datepicker-time',true).first();
23496         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23497         
23498         this.picker().on('mousedown', this.onMousedown, this);
23499         this.picker().on('click', this.onClick, this);
23500         
23501         this.picker().addClass('datepicker-dropdown');
23502     
23503         this.fillTime();
23504         this.update();
23505             
23506         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23507         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23508         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23509         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23510         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23511         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23512
23513     },
23514     
23515     fireKey: function(e){
23516         if (!this.picker().isVisible()){
23517             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23518                 this.show();
23519             }
23520             return;
23521         }
23522
23523         e.preventDefault();
23524         
23525         switch(e.keyCode){
23526             case 27: // escape
23527                 this.hide();
23528                 break;
23529             case 37: // left
23530             case 39: // right
23531                 this.onTogglePeriod();
23532                 break;
23533             case 38: // up
23534                 this.onIncrementMinutes();
23535                 break;
23536             case 40: // down
23537                 this.onDecrementMinutes();
23538                 break;
23539             case 13: // enter
23540             case 9: // tab
23541                 this.setTime();
23542                 break;
23543         }
23544     },
23545     
23546     onClick: function(e) {
23547         e.stopPropagation();
23548         e.preventDefault();
23549     },
23550     
23551     picker : function()
23552     {
23553         return this.pickerEl;
23554     },
23555     
23556     fillTime: function()
23557     {    
23558         var time = this.pop.select('tbody', true).first();
23559         
23560         time.dom.innerHTML = '';
23561         
23562         time.createChild({
23563             tag: 'tr',
23564             cn: [
23565                 {
23566                     tag: 'td',
23567                     cn: [
23568                         {
23569                             tag: 'a',
23570                             href: '#',
23571                             cls: 'btn',
23572                             cn: [
23573                                 {
23574                                     tag: 'i',
23575                                     cls: 'hours-up fa fas fa-chevron-up'
23576                                 }
23577                             ]
23578                         } 
23579                     ]
23580                 },
23581                 {
23582                     tag: 'td',
23583                     cls: 'separator'
23584                 },
23585                 {
23586                     tag: 'td',
23587                     cn: [
23588                         {
23589                             tag: 'a',
23590                             href: '#',
23591                             cls: 'btn',
23592                             cn: [
23593                                 {
23594                                     tag: 'i',
23595                                     cls: 'minutes-up fa fas fa-chevron-up'
23596                                 }
23597                             ]
23598                         }
23599                     ]
23600                 },
23601                 {
23602                     tag: 'td',
23603                     cls: 'separator'
23604                 }
23605             ]
23606         });
23607         
23608         time.createChild({
23609             tag: 'tr',
23610             cn: [
23611                 {
23612                     tag: 'td',
23613                     cn: [
23614                         {
23615                             tag: 'span',
23616                             cls: 'timepicker-hour',
23617                             html: '00'
23618                         }  
23619                     ]
23620                 },
23621                 {
23622                     tag: 'td',
23623                     cls: 'separator',
23624                     html: ':'
23625                 },
23626                 {
23627                     tag: 'td',
23628                     cn: [
23629                         {
23630                             tag: 'span',
23631                             cls: 'timepicker-minute',
23632                             html: '00'
23633                         }  
23634                     ]
23635                 },
23636                 {
23637                     tag: 'td',
23638                     cls: 'separator'
23639                 },
23640                 {
23641                     tag: 'td',
23642                     cn: [
23643                         {
23644                             tag: 'button',
23645                             type: 'button',
23646                             cls: 'btn btn-primary period',
23647                             html: 'AM'
23648                             
23649                         }
23650                     ]
23651                 }
23652             ]
23653         });
23654         
23655         time.createChild({
23656             tag: 'tr',
23657             cn: [
23658                 {
23659                     tag: 'td',
23660                     cn: [
23661                         {
23662                             tag: 'a',
23663                             href: '#',
23664                             cls: 'btn',
23665                             cn: [
23666                                 {
23667                                     tag: 'span',
23668                                     cls: 'hours-down fa fas fa-chevron-down'
23669                                 }
23670                             ]
23671                         }
23672                     ]
23673                 },
23674                 {
23675                     tag: 'td',
23676                     cls: 'separator'
23677                 },
23678                 {
23679                     tag: 'td',
23680                     cn: [
23681                         {
23682                             tag: 'a',
23683                             href: '#',
23684                             cls: 'btn',
23685                             cn: [
23686                                 {
23687                                     tag: 'span',
23688                                     cls: 'minutes-down fa fas fa-chevron-down'
23689                                 }
23690                             ]
23691                         }
23692                     ]
23693                 },
23694                 {
23695                     tag: 'td',
23696                     cls: 'separator'
23697                 }
23698             ]
23699         });
23700         
23701     },
23702     
23703     update: function()
23704     {
23705         
23706         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23707         
23708         this.fill();
23709     },
23710     
23711     fill: function() 
23712     {
23713         var hours = this.time.getHours();
23714         var minutes = this.time.getMinutes();
23715         var period = 'AM';
23716         
23717         if(hours > 11){
23718             period = 'PM';
23719         }
23720         
23721         if(hours == 0){
23722             hours = 12;
23723         }
23724         
23725         
23726         if(hours > 12){
23727             hours = hours - 12;
23728         }
23729         
23730         if(hours < 10){
23731             hours = '0' + hours;
23732         }
23733         
23734         if(minutes < 10){
23735             minutes = '0' + minutes;
23736         }
23737         
23738         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23739         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23740         this.pop.select('button', true).first().dom.innerHTML = period;
23741         
23742     },
23743     
23744     place: function()
23745     {   
23746         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23747         
23748         var cls = ['bottom'];
23749         
23750         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23751             cls.pop();
23752             cls.push('top');
23753         }
23754         
23755         cls.push('right');
23756         
23757         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23758             cls.pop();
23759             cls.push('left');
23760         }
23761         //this.picker().setXY(20000,20000);
23762         this.picker().addClass(cls.join('-'));
23763         
23764         var _this = this;
23765         
23766         Roo.each(cls, function(c){
23767             if(c == 'bottom'){
23768                 (function() {
23769                  //  
23770                 }).defer(200);
23771                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23772                 //_this.picker().setTop(_this.inputEl().getHeight());
23773                 return;
23774             }
23775             if(c == 'top'){
23776                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23777                 
23778                 //_this.picker().setTop(0 - _this.picker().getHeight());
23779                 return;
23780             }
23781             /*
23782             if(c == 'left'){
23783                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23784                 return;
23785             }
23786             if(c == 'right'){
23787                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23788                 return;
23789             }
23790             */
23791         });
23792         
23793     },
23794   
23795     onFocus : function()
23796     {
23797         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23798         this.show();
23799     },
23800     
23801     onBlur : function()
23802     {
23803         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23804         this.hide();
23805     },
23806     
23807     show : function()
23808     {
23809         this.picker().show();
23810         this.pop.show();
23811         this.update();
23812         this.place();
23813         
23814         this.fireEvent('show', this, this.date);
23815     },
23816     
23817     hide : function()
23818     {
23819         this.picker().hide();
23820         this.pop.hide();
23821         
23822         this.fireEvent('hide', this, this.date);
23823     },
23824     
23825     setTime : function()
23826     {
23827         this.hide();
23828         this.setValue(this.time.format(this.format));
23829         
23830         this.fireEvent('select', this, this.date);
23831         
23832         
23833     },
23834     
23835     onMousedown: function(e){
23836         e.stopPropagation();
23837         e.preventDefault();
23838     },
23839     
23840     onIncrementHours: function()
23841     {
23842         Roo.log('onIncrementHours');
23843         this.time = this.time.add(Date.HOUR, 1);
23844         this.update();
23845         
23846     },
23847     
23848     onDecrementHours: function()
23849     {
23850         Roo.log('onDecrementHours');
23851         this.time = this.time.add(Date.HOUR, -1);
23852         this.update();
23853     },
23854     
23855     onIncrementMinutes: function()
23856     {
23857         Roo.log('onIncrementMinutes');
23858         this.time = this.time.add(Date.MINUTE, 1);
23859         this.update();
23860     },
23861     
23862     onDecrementMinutes: function()
23863     {
23864         Roo.log('onDecrementMinutes');
23865         this.time = this.time.add(Date.MINUTE, -1);
23866         this.update();
23867     },
23868     
23869     onTogglePeriod: function()
23870     {
23871         Roo.log('onTogglePeriod');
23872         this.time = this.time.add(Date.HOUR, 12);
23873         this.update();
23874     }
23875     
23876    
23877 });
23878  
23879
23880 Roo.apply(Roo.bootstrap.TimeField,  {
23881   
23882     template : {
23883         tag: 'div',
23884         cls: 'datepicker dropdown-menu',
23885         cn: [
23886             {
23887                 tag: 'div',
23888                 cls: 'datepicker-time',
23889                 cn: [
23890                 {
23891                     tag: 'table',
23892                     cls: 'table-condensed',
23893                     cn:[
23894                         {
23895                             tag: 'tbody',
23896                             cn: [
23897                                 {
23898                                     tag: 'tr',
23899                                     cn: [
23900                                     {
23901                                         tag: 'td',
23902                                         colspan: '7'
23903                                     }
23904                                     ]
23905                                 }
23906                             ]
23907                         },
23908                         {
23909                             tag: 'tfoot',
23910                             cn: [
23911                                 {
23912                                     tag: 'tr',
23913                                     cn: [
23914                                     {
23915                                         tag: 'th',
23916                                         colspan: '7',
23917                                         cls: '',
23918                                         cn: [
23919                                             {
23920                                                 tag: 'button',
23921                                                 cls: 'btn btn-info ok',
23922                                                 html: 'OK'
23923                                             }
23924                                         ]
23925                                     }
23926                     
23927                                     ]
23928                                 }
23929                             ]
23930                         }
23931                     ]
23932                 }
23933                 ]
23934             }
23935         ]
23936     }
23937 });
23938
23939  
23940
23941  /*
23942  * - LGPL
23943  *
23944  * MonthField
23945  * 
23946  */
23947
23948 /**
23949  * @class Roo.bootstrap.MonthField
23950  * @extends Roo.bootstrap.Input
23951  * Bootstrap MonthField class
23952  * 
23953  * @cfg {String} language default en
23954  * 
23955  * @constructor
23956  * Create a new MonthField
23957  * @param {Object} config The config object
23958  */
23959
23960 Roo.bootstrap.MonthField = function(config){
23961     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23962     
23963     this.addEvents({
23964         /**
23965          * @event show
23966          * Fires when this field show.
23967          * @param {Roo.bootstrap.MonthField} this
23968          * @param {Mixed} date The date value
23969          */
23970         show : true,
23971         /**
23972          * @event show
23973          * Fires when this field hide.
23974          * @param {Roo.bootstrap.MonthField} this
23975          * @param {Mixed} date The date value
23976          */
23977         hide : true,
23978         /**
23979          * @event select
23980          * Fires when select a date.
23981          * @param {Roo.bootstrap.MonthField} this
23982          * @param {String} oldvalue The old value
23983          * @param {String} newvalue The new value
23984          */
23985         select : true
23986     });
23987 };
23988
23989 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23990     
23991     onRender: function(ct, position)
23992     {
23993         
23994         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23995         
23996         this.language = this.language || 'en';
23997         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23998         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23999         
24000         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24001         this.isInline = false;
24002         this.isInput = true;
24003         this.component = this.el.select('.add-on', true).first() || false;
24004         this.component = (this.component && this.component.length === 0) ? false : this.component;
24005         this.hasInput = this.component && this.inputEL().length;
24006         
24007         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24008         
24009         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24010         
24011         this.picker().on('mousedown', this.onMousedown, this);
24012         this.picker().on('click', this.onClick, this);
24013         
24014         this.picker().addClass('datepicker-dropdown');
24015         
24016         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24017             v.setStyle('width', '189px');
24018         });
24019         
24020         this.fillMonths();
24021         
24022         this.update();
24023         
24024         if(this.isInline) {
24025             this.show();
24026         }
24027         
24028     },
24029     
24030     setValue: function(v, suppressEvent)
24031     {   
24032         var o = this.getValue();
24033         
24034         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24035         
24036         this.update();
24037
24038         if(suppressEvent !== true){
24039             this.fireEvent('select', this, o, v);
24040         }
24041         
24042     },
24043     
24044     getValue: function()
24045     {
24046         return this.value;
24047     },
24048     
24049     onClick: function(e) 
24050     {
24051         e.stopPropagation();
24052         e.preventDefault();
24053         
24054         var target = e.getTarget();
24055         
24056         if(target.nodeName.toLowerCase() === 'i'){
24057             target = Roo.get(target).dom.parentNode;
24058         }
24059         
24060         var nodeName = target.nodeName;
24061         var className = target.className;
24062         var html = target.innerHTML;
24063         
24064         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24065             return;
24066         }
24067         
24068         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24069         
24070         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24071         
24072         this.hide();
24073                         
24074     },
24075     
24076     picker : function()
24077     {
24078         return this.pickerEl;
24079     },
24080     
24081     fillMonths: function()
24082     {    
24083         var i = 0;
24084         var months = this.picker().select('>.datepicker-months td', true).first();
24085         
24086         months.dom.innerHTML = '';
24087         
24088         while (i < 12) {
24089             var month = {
24090                 tag: 'span',
24091                 cls: 'month',
24092                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24093             };
24094             
24095             months.createChild(month);
24096         }
24097         
24098     },
24099     
24100     update: function()
24101     {
24102         var _this = this;
24103         
24104         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24105             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24106         }
24107         
24108         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24109             e.removeClass('active');
24110             
24111             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24112                 e.addClass('active');
24113             }
24114         })
24115     },
24116     
24117     place: function()
24118     {
24119         if(this.isInline) {
24120             return;
24121         }
24122         
24123         this.picker().removeClass(['bottom', 'top']);
24124         
24125         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24126             /*
24127              * place to the top of element!
24128              *
24129              */
24130             
24131             this.picker().addClass('top');
24132             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24133             
24134             return;
24135         }
24136         
24137         this.picker().addClass('bottom');
24138         
24139         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24140     },
24141     
24142     onFocus : function()
24143     {
24144         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24145         this.show();
24146     },
24147     
24148     onBlur : function()
24149     {
24150         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24151         
24152         var d = this.inputEl().getValue();
24153         
24154         this.setValue(d);
24155                 
24156         this.hide();
24157     },
24158     
24159     show : function()
24160     {
24161         this.picker().show();
24162         this.picker().select('>.datepicker-months', true).first().show();
24163         this.update();
24164         this.place();
24165         
24166         this.fireEvent('show', this, this.date);
24167     },
24168     
24169     hide : function()
24170     {
24171         if(this.isInline) {
24172             return;
24173         }
24174         this.picker().hide();
24175         this.fireEvent('hide', this, this.date);
24176         
24177     },
24178     
24179     onMousedown: function(e)
24180     {
24181         e.stopPropagation();
24182         e.preventDefault();
24183     },
24184     
24185     keyup: function(e)
24186     {
24187         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24188         this.update();
24189     },
24190
24191     fireKey: function(e)
24192     {
24193         if (!this.picker().isVisible()){
24194             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24195                 this.show();
24196             }
24197             return;
24198         }
24199         
24200         var dir;
24201         
24202         switch(e.keyCode){
24203             case 27: // escape
24204                 this.hide();
24205                 e.preventDefault();
24206                 break;
24207             case 37: // left
24208             case 39: // right
24209                 dir = e.keyCode == 37 ? -1 : 1;
24210                 
24211                 this.vIndex = this.vIndex + dir;
24212                 
24213                 if(this.vIndex < 0){
24214                     this.vIndex = 0;
24215                 }
24216                 
24217                 if(this.vIndex > 11){
24218                     this.vIndex = 11;
24219                 }
24220                 
24221                 if(isNaN(this.vIndex)){
24222                     this.vIndex = 0;
24223                 }
24224                 
24225                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24226                 
24227                 break;
24228             case 38: // up
24229             case 40: // down
24230                 
24231                 dir = e.keyCode == 38 ? -1 : 1;
24232                 
24233                 this.vIndex = this.vIndex + dir * 4;
24234                 
24235                 if(this.vIndex < 0){
24236                     this.vIndex = 0;
24237                 }
24238                 
24239                 if(this.vIndex > 11){
24240                     this.vIndex = 11;
24241                 }
24242                 
24243                 if(isNaN(this.vIndex)){
24244                     this.vIndex = 0;
24245                 }
24246                 
24247                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24248                 break;
24249                 
24250             case 13: // enter
24251                 
24252                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24253                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24254                 }
24255                 
24256                 this.hide();
24257                 e.preventDefault();
24258                 break;
24259             case 9: // tab
24260                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24261                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24262                 }
24263                 this.hide();
24264                 break;
24265             case 16: // shift
24266             case 17: // ctrl
24267             case 18: // alt
24268                 break;
24269             default :
24270                 this.hide();
24271                 
24272         }
24273     },
24274     
24275     remove: function() 
24276     {
24277         this.picker().remove();
24278     }
24279    
24280 });
24281
24282 Roo.apply(Roo.bootstrap.MonthField,  {
24283     
24284     content : {
24285         tag: 'tbody',
24286         cn: [
24287         {
24288             tag: 'tr',
24289             cn: [
24290             {
24291                 tag: 'td',
24292                 colspan: '7'
24293             }
24294             ]
24295         }
24296         ]
24297     },
24298     
24299     dates:{
24300         en: {
24301             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24302             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24303         }
24304     }
24305 });
24306
24307 Roo.apply(Roo.bootstrap.MonthField,  {
24308   
24309     template : {
24310         tag: 'div',
24311         cls: 'datepicker dropdown-menu roo-dynamic',
24312         cn: [
24313             {
24314                 tag: 'div',
24315                 cls: 'datepicker-months',
24316                 cn: [
24317                 {
24318                     tag: 'table',
24319                     cls: 'table-condensed',
24320                     cn:[
24321                         Roo.bootstrap.DateField.content
24322                     ]
24323                 }
24324                 ]
24325             }
24326         ]
24327     }
24328 });
24329
24330  
24331
24332  
24333  /*
24334  * - LGPL
24335  *
24336  * CheckBox
24337  * 
24338  */
24339
24340 /**
24341  * @class Roo.bootstrap.CheckBox
24342  * @extends Roo.bootstrap.Input
24343  * Bootstrap CheckBox class
24344  * 
24345  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24346  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24347  * @cfg {String} boxLabel The text that appears beside the checkbox
24348  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24349  * @cfg {Boolean} checked initnal the element
24350  * @cfg {Boolean} inline inline the element (default false)
24351  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24352  * @cfg {String} tooltip label tooltip
24353  * 
24354  * @constructor
24355  * Create a new CheckBox
24356  * @param {Object} config The config object
24357  */
24358
24359 Roo.bootstrap.CheckBox = function(config){
24360     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24361    
24362     this.addEvents({
24363         /**
24364         * @event check
24365         * Fires when the element is checked or unchecked.
24366         * @param {Roo.bootstrap.CheckBox} this This input
24367         * @param {Boolean} checked The new checked value
24368         */
24369        check : true,
24370        /**
24371         * @event click
24372         * Fires when the element is click.
24373         * @param {Roo.bootstrap.CheckBox} this This input
24374         */
24375        click : true
24376     });
24377     
24378 };
24379
24380 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24381   
24382     inputType: 'checkbox',
24383     inputValue: 1,
24384     valueOff: 0,
24385     boxLabel: false,
24386     checked: false,
24387     weight : false,
24388     inline: false,
24389     tooltip : '',
24390     
24391     // checkbox success does not make any sense really.. 
24392     invalidClass : "",
24393     validClass : "",
24394     
24395     
24396     getAutoCreate : function()
24397     {
24398         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24399         
24400         var id = Roo.id();
24401         
24402         var cfg = {};
24403         
24404         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24405         
24406         if(this.inline){
24407             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24408         }
24409         
24410         var input =  {
24411             tag: 'input',
24412             id : id,
24413             type : this.inputType,
24414             value : this.inputValue,
24415             cls : 'roo-' + this.inputType, //'form-box',
24416             placeholder : this.placeholder || ''
24417             
24418         };
24419         
24420         if(this.inputType != 'radio'){
24421             var hidden =  {
24422                 tag: 'input',
24423                 type : 'hidden',
24424                 cls : 'roo-hidden-value',
24425                 value : this.checked ? this.inputValue : this.valueOff
24426             };
24427         }
24428         
24429             
24430         if (this.weight) { // Validity check?
24431             cfg.cls += " " + this.inputType + "-" + this.weight;
24432         }
24433         
24434         if (this.disabled) {
24435             input.disabled=true;
24436         }
24437         
24438         if(this.checked){
24439             input.checked = this.checked;
24440         }
24441         
24442         if (this.name) {
24443             
24444             input.name = this.name;
24445             
24446             if(this.inputType != 'radio'){
24447                 hidden.name = this.name;
24448                 input.name = '_hidden_' + this.name;
24449             }
24450         }
24451         
24452         if (this.size) {
24453             input.cls += ' input-' + this.size;
24454         }
24455         
24456         var settings=this;
24457         
24458         ['xs','sm','md','lg'].map(function(size){
24459             if (settings[size]) {
24460                 cfg.cls += ' col-' + size + '-' + settings[size];
24461             }
24462         });
24463         
24464         var inputblock = input;
24465          
24466         if (this.before || this.after) {
24467             
24468             inputblock = {
24469                 cls : 'input-group',
24470                 cn :  [] 
24471             };
24472             
24473             if (this.before) {
24474                 inputblock.cn.push({
24475                     tag :'span',
24476                     cls : 'input-group-addon',
24477                     html : this.before
24478                 });
24479             }
24480             
24481             inputblock.cn.push(input);
24482             
24483             if(this.inputType != 'radio'){
24484                 inputblock.cn.push(hidden);
24485             }
24486             
24487             if (this.after) {
24488                 inputblock.cn.push({
24489                     tag :'span',
24490                     cls : 'input-group-addon',
24491                     html : this.after
24492                 });
24493             }
24494             
24495         }
24496         var boxLabelCfg = false;
24497         
24498         if(this.boxLabel){
24499            
24500             boxLabelCfg = {
24501                 tag: 'label',
24502                 //'for': id, // box label is handled by onclick - so no for...
24503                 cls: 'box-label',
24504                 html: this.boxLabel
24505             };
24506             if(this.tooltip){
24507                 boxLabelCfg.tooltip = this.tooltip;
24508             }
24509              
24510         }
24511         
24512         
24513         if (align ==='left' && this.fieldLabel.length) {
24514 //                Roo.log("left and has label");
24515             cfg.cn = [
24516                 {
24517                     tag: 'label',
24518                     'for' :  id,
24519                     cls : 'control-label',
24520                     html : this.fieldLabel
24521                 },
24522                 {
24523                     cls : "", 
24524                     cn: [
24525                         inputblock
24526                     ]
24527                 }
24528             ];
24529             
24530             if (boxLabelCfg) {
24531                 cfg.cn[1].cn.push(boxLabelCfg);
24532             }
24533             
24534             if(this.labelWidth > 12){
24535                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24536             }
24537             
24538             if(this.labelWidth < 13 && this.labelmd == 0){
24539                 this.labelmd = this.labelWidth;
24540             }
24541             
24542             if(this.labellg > 0){
24543                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24544                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24545             }
24546             
24547             if(this.labelmd > 0){
24548                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24549                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24550             }
24551             
24552             if(this.labelsm > 0){
24553                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24554                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24555             }
24556             
24557             if(this.labelxs > 0){
24558                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24559                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24560             }
24561             
24562         } else if ( this.fieldLabel.length) {
24563 //                Roo.log(" label");
24564                 cfg.cn = [
24565                    
24566                     {
24567                         tag: this.boxLabel ? 'span' : 'label',
24568                         'for': id,
24569                         cls: 'control-label box-input-label',
24570                         //cls : 'input-group-addon',
24571                         html : this.fieldLabel
24572                     },
24573                     
24574                     inputblock
24575                     
24576                 ];
24577                 if (boxLabelCfg) {
24578                     cfg.cn.push(boxLabelCfg);
24579                 }
24580
24581         } else {
24582             
24583 //                Roo.log(" no label && no align");
24584                 cfg.cn = [  inputblock ] ;
24585                 if (boxLabelCfg) {
24586                     cfg.cn.push(boxLabelCfg);
24587                 }
24588
24589                 
24590         }
24591         
24592        
24593         
24594         if(this.inputType != 'radio'){
24595             cfg.cn.push(hidden);
24596         }
24597         
24598         return cfg;
24599         
24600     },
24601     
24602     /**
24603      * return the real input element.
24604      */
24605     inputEl: function ()
24606     {
24607         return this.el.select('input.roo-' + this.inputType,true).first();
24608     },
24609     hiddenEl: function ()
24610     {
24611         return this.el.select('input.roo-hidden-value',true).first();
24612     },
24613     
24614     labelEl: function()
24615     {
24616         return this.el.select('label.control-label',true).first();
24617     },
24618     /* depricated... */
24619     
24620     label: function()
24621     {
24622         return this.labelEl();
24623     },
24624     
24625     boxLabelEl: function()
24626     {
24627         return this.el.select('label.box-label',true).first();
24628     },
24629     
24630     initEvents : function()
24631     {
24632 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24633         
24634         this.inputEl().on('click', this.onClick,  this);
24635         
24636         if (this.boxLabel) { 
24637             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24638         }
24639         
24640         this.startValue = this.getValue();
24641         
24642         if(this.groupId){
24643             Roo.bootstrap.CheckBox.register(this);
24644         }
24645     },
24646     
24647     onClick : function(e)
24648     {   
24649         if(this.fireEvent('click', this, e) !== false){
24650             this.setChecked(!this.checked);
24651         }
24652         
24653     },
24654     
24655     setChecked : function(state,suppressEvent)
24656     {
24657         this.startValue = this.getValue();
24658
24659         if(this.inputType == 'radio'){
24660             
24661             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24662                 e.dom.checked = false;
24663             });
24664             
24665             this.inputEl().dom.checked = true;
24666             
24667             this.inputEl().dom.value = this.inputValue;
24668             
24669             if(suppressEvent !== true){
24670                 this.fireEvent('check', this, true);
24671             }
24672             
24673             this.validate();
24674             
24675             return;
24676         }
24677         
24678         this.checked = state;
24679         
24680         this.inputEl().dom.checked = state;
24681         
24682         
24683         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24684         
24685         if(suppressEvent !== true){
24686             this.fireEvent('check', this, state);
24687         }
24688         
24689         this.validate();
24690     },
24691     
24692     getValue : function()
24693     {
24694         if(this.inputType == 'radio'){
24695             return this.getGroupValue();
24696         }
24697         
24698         return this.hiddenEl().dom.value;
24699         
24700     },
24701     
24702     getGroupValue : function()
24703     {
24704         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24705             return '';
24706         }
24707         
24708         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24709     },
24710     
24711     setValue : function(v,suppressEvent)
24712     {
24713         if(this.inputType == 'radio'){
24714             this.setGroupValue(v, suppressEvent);
24715             return;
24716         }
24717         
24718         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24719         
24720         this.validate();
24721     },
24722     
24723     setGroupValue : function(v, suppressEvent)
24724     {
24725         this.startValue = this.getValue();
24726         
24727         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24728             e.dom.checked = false;
24729             
24730             if(e.dom.value == v){
24731                 e.dom.checked = true;
24732             }
24733         });
24734         
24735         if(suppressEvent !== true){
24736             this.fireEvent('check', this, true);
24737         }
24738
24739         this.validate();
24740         
24741         return;
24742     },
24743     
24744     validate : function()
24745     {
24746         if(this.getVisibilityEl().hasClass('hidden')){
24747             return true;
24748         }
24749         
24750         if(
24751                 this.disabled || 
24752                 (this.inputType == 'radio' && this.validateRadio()) ||
24753                 (this.inputType == 'checkbox' && this.validateCheckbox())
24754         ){
24755             this.markValid();
24756             return true;
24757         }
24758         
24759         this.markInvalid();
24760         return false;
24761     },
24762     
24763     validateRadio : function()
24764     {
24765         if(this.getVisibilityEl().hasClass('hidden')){
24766             return true;
24767         }
24768         
24769         if(this.allowBlank){
24770             return true;
24771         }
24772         
24773         var valid = false;
24774         
24775         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24776             if(!e.dom.checked){
24777                 return;
24778             }
24779             
24780             valid = true;
24781             
24782             return false;
24783         });
24784         
24785         return valid;
24786     },
24787     
24788     validateCheckbox : function()
24789     {
24790         if(!this.groupId){
24791             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24792             //return (this.getValue() == this.inputValue) ? true : false;
24793         }
24794         
24795         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24796         
24797         if(!group){
24798             return false;
24799         }
24800         
24801         var r = false;
24802         
24803         for(var i in group){
24804             if(group[i].el.isVisible(true)){
24805                 r = false;
24806                 break;
24807             }
24808             
24809             r = true;
24810         }
24811         
24812         for(var i in group){
24813             if(r){
24814                 break;
24815             }
24816             
24817             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24818         }
24819         
24820         return r;
24821     },
24822     
24823     /**
24824      * Mark this field as valid
24825      */
24826     markValid : function()
24827     {
24828         var _this = this;
24829         
24830         this.fireEvent('valid', this);
24831         
24832         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24833         
24834         if(this.groupId){
24835             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24836         }
24837         
24838         if(label){
24839             label.markValid();
24840         }
24841
24842         if(this.inputType == 'radio'){
24843             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24844                 var fg = e.findParent('.form-group', false, true);
24845                 if (Roo.bootstrap.version == 3) {
24846                     fg.removeClass([_this.invalidClass, _this.validClass]);
24847                     fg.addClass(_this.validClass);
24848                 } else {
24849                     fg.removeClass(['is-valid', 'is-invalid']);
24850                     fg.addClass('is-valid');
24851                 }
24852             });
24853             
24854             return;
24855         }
24856
24857         if(!this.groupId){
24858             var fg = this.el.findParent('.form-group', false, true);
24859             if (Roo.bootstrap.version == 3) {
24860                 fg.removeClass([this.invalidClass, this.validClass]);
24861                 fg.addClass(this.validClass);
24862             } else {
24863                 fg.removeClass(['is-valid', 'is-invalid']);
24864                 fg.addClass('is-valid');
24865             }
24866             return;
24867         }
24868         
24869         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24870         
24871         if(!group){
24872             return;
24873         }
24874         
24875         for(var i in group){
24876             var fg = group[i].el.findParent('.form-group', false, true);
24877             if (Roo.bootstrap.version == 3) {
24878                 fg.removeClass([this.invalidClass, this.validClass]);
24879                 fg.addClass(this.validClass);
24880             } else {
24881                 fg.removeClass(['is-valid', 'is-invalid']);
24882                 fg.addClass('is-valid');
24883             }
24884         }
24885     },
24886     
24887      /**
24888      * Mark this field as invalid
24889      * @param {String} msg The validation message
24890      */
24891     markInvalid : function(msg)
24892     {
24893         if(this.allowBlank){
24894             return;
24895         }
24896         
24897         var _this = this;
24898         
24899         this.fireEvent('invalid', this, msg);
24900         
24901         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24902         
24903         if(this.groupId){
24904             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24905         }
24906         
24907         if(label){
24908             label.markInvalid();
24909         }
24910             
24911         if(this.inputType == 'radio'){
24912             
24913             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24914                 var fg = e.findParent('.form-group', false, true);
24915                 if (Roo.bootstrap.version == 3) {
24916                     fg.removeClass([_this.invalidClass, _this.validClass]);
24917                     fg.addClass(_this.invalidClass);
24918                 } else {
24919                     fg.removeClass(['is-invalid', 'is-valid']);
24920                     fg.addClass('is-invalid');
24921                 }
24922             });
24923             
24924             return;
24925         }
24926         
24927         if(!this.groupId){
24928             var fg = this.el.findParent('.form-group', false, true);
24929             if (Roo.bootstrap.version == 3) {
24930                 fg.removeClass([_this.invalidClass, _this.validClass]);
24931                 fg.addClass(_this.invalidClass);
24932             } else {
24933                 fg.removeClass(['is-invalid', 'is-valid']);
24934                 fg.addClass('is-invalid');
24935             }
24936             return;
24937         }
24938         
24939         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24940         
24941         if(!group){
24942             return;
24943         }
24944         
24945         for(var i in group){
24946             var fg = group[i].el.findParent('.form-group', false, true);
24947             if (Roo.bootstrap.version == 3) {
24948                 fg.removeClass([_this.invalidClass, _this.validClass]);
24949                 fg.addClass(_this.invalidClass);
24950             } else {
24951                 fg.removeClass(['is-invalid', 'is-valid']);
24952                 fg.addClass('is-invalid');
24953             }
24954         }
24955         
24956     },
24957     
24958     clearInvalid : function()
24959     {
24960         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24961         
24962         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24963         
24964         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24965         
24966         if (label && label.iconEl) {
24967             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24968             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24969         }
24970     },
24971     
24972     disable : function()
24973     {
24974         if(this.inputType != 'radio'){
24975             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24976             return;
24977         }
24978         
24979         var _this = this;
24980         
24981         if(this.rendered){
24982             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24983                 _this.getActionEl().addClass(this.disabledClass);
24984                 e.dom.disabled = true;
24985             });
24986         }
24987         
24988         this.disabled = true;
24989         this.fireEvent("disable", this);
24990         return this;
24991     },
24992
24993     enable : function()
24994     {
24995         if(this.inputType != 'radio'){
24996             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24997             return;
24998         }
24999         
25000         var _this = this;
25001         
25002         if(this.rendered){
25003             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25004                 _this.getActionEl().removeClass(this.disabledClass);
25005                 e.dom.disabled = false;
25006             });
25007         }
25008         
25009         this.disabled = false;
25010         this.fireEvent("enable", this);
25011         return this;
25012     },
25013     
25014     setBoxLabel : function(v)
25015     {
25016         this.boxLabel = v;
25017         
25018         if(this.rendered){
25019             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25020         }
25021     }
25022
25023 });
25024
25025 Roo.apply(Roo.bootstrap.CheckBox, {
25026     
25027     groups: {},
25028     
25029      /**
25030     * register a CheckBox Group
25031     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25032     */
25033     register : function(checkbox)
25034     {
25035         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25036             this.groups[checkbox.groupId] = {};
25037         }
25038         
25039         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25040             return;
25041         }
25042         
25043         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25044         
25045     },
25046     /**
25047     * fetch a CheckBox Group based on the group ID
25048     * @param {string} the group ID
25049     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25050     */
25051     get: function(groupId) {
25052         if (typeof(this.groups[groupId]) == 'undefined') {
25053             return false;
25054         }
25055         
25056         return this.groups[groupId] ;
25057     }
25058     
25059     
25060 });
25061 /*
25062  * - LGPL
25063  *
25064  * RadioItem
25065  * 
25066  */
25067
25068 /**
25069  * @class Roo.bootstrap.Radio
25070  * @extends Roo.bootstrap.Component
25071  * Bootstrap Radio class
25072  * @cfg {String} boxLabel - the label associated
25073  * @cfg {String} value - the value of radio
25074  * 
25075  * @constructor
25076  * Create a new Radio
25077  * @param {Object} config The config object
25078  */
25079 Roo.bootstrap.Radio = function(config){
25080     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25081     
25082 };
25083
25084 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25085     
25086     boxLabel : '',
25087     
25088     value : '',
25089     
25090     getAutoCreate : function()
25091     {
25092         var cfg = {
25093             tag : 'div',
25094             cls : 'form-group radio',
25095             cn : [
25096                 {
25097                     tag : 'label',
25098                     cls : 'box-label',
25099                     html : this.boxLabel
25100                 }
25101             ]
25102         };
25103         
25104         return cfg;
25105     },
25106     
25107     initEvents : function() 
25108     {
25109         this.parent().register(this);
25110         
25111         this.el.on('click', this.onClick, this);
25112         
25113     },
25114     
25115     onClick : function(e)
25116     {
25117         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25118             this.setChecked(true);
25119         }
25120     },
25121     
25122     setChecked : function(state, suppressEvent)
25123     {
25124         this.parent().setValue(this.value, suppressEvent);
25125         
25126     },
25127     
25128     setBoxLabel : function(v)
25129     {
25130         this.boxLabel = v;
25131         
25132         if(this.rendered){
25133             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25134         }
25135     }
25136     
25137 });
25138  
25139
25140  /*
25141  * - LGPL
25142  *
25143  * Input
25144  * 
25145  */
25146
25147 /**
25148  * @class Roo.bootstrap.SecurePass
25149  * @extends Roo.bootstrap.Input
25150  * Bootstrap SecurePass class
25151  *
25152  * 
25153  * @constructor
25154  * Create a new SecurePass
25155  * @param {Object} config The config object
25156  */
25157  
25158 Roo.bootstrap.SecurePass = function (config) {
25159     // these go here, so the translation tool can replace them..
25160     this.errors = {
25161         PwdEmpty: "Please type a password, and then retype it to confirm.",
25162         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25163         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25164         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25165         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25166         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25167         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25168         TooWeak: "Your password is Too Weak."
25169     },
25170     this.meterLabel = "Password strength:";
25171     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25172     this.meterClass = [
25173         "roo-password-meter-tooweak", 
25174         "roo-password-meter-weak", 
25175         "roo-password-meter-medium", 
25176         "roo-password-meter-strong", 
25177         "roo-password-meter-grey"
25178     ];
25179     
25180     this.errors = {};
25181     
25182     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25183 }
25184
25185 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25186     /**
25187      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25188      * {
25189      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25190      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25191      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25192      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25193      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25194      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25195      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25196      * })
25197      */
25198     // private
25199     
25200     meterWidth: 300,
25201     errorMsg :'',    
25202     errors: false,
25203     imageRoot: '/',
25204     /**
25205      * @cfg {String/Object} Label for the strength meter (defaults to
25206      * 'Password strength:')
25207      */
25208     // private
25209     meterLabel: '',
25210     /**
25211      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25212      * ['Weak', 'Medium', 'Strong'])
25213      */
25214     // private    
25215     pwdStrengths: false,    
25216     // private
25217     strength: 0,
25218     // private
25219     _lastPwd: null,
25220     // private
25221     kCapitalLetter: 0,
25222     kSmallLetter: 1,
25223     kDigit: 2,
25224     kPunctuation: 3,
25225     
25226     insecure: false,
25227     // private
25228     initEvents: function ()
25229     {
25230         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25231
25232         if (this.el.is('input[type=password]') && Roo.isSafari) {
25233             this.el.on('keydown', this.SafariOnKeyDown, this);
25234         }
25235
25236         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25237     },
25238     // private
25239     onRender: function (ct, position)
25240     {
25241         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25242         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25243         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25244
25245         this.trigger.createChild({
25246                    cn: [
25247                     {
25248                     //id: 'PwdMeter',
25249                     tag: 'div',
25250                     cls: 'roo-password-meter-grey col-xs-12',
25251                     style: {
25252                         //width: 0,
25253                         //width: this.meterWidth + 'px'                                                
25254                         }
25255                     },
25256                     {                            
25257                          cls: 'roo-password-meter-text'                          
25258                     }
25259                 ]            
25260         });
25261
25262          
25263         if (this.hideTrigger) {
25264             this.trigger.setDisplayed(false);
25265         }
25266         this.setSize(this.width || '', this.height || '');
25267     },
25268     // private
25269     onDestroy: function ()
25270     {
25271         if (this.trigger) {
25272             this.trigger.removeAllListeners();
25273             this.trigger.remove();
25274         }
25275         if (this.wrap) {
25276             this.wrap.remove();
25277         }
25278         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25279     },
25280     // private
25281     checkStrength: function ()
25282     {
25283         var pwd = this.inputEl().getValue();
25284         if (pwd == this._lastPwd) {
25285             return;
25286         }
25287
25288         var strength;
25289         if (this.ClientSideStrongPassword(pwd)) {
25290             strength = 3;
25291         } else if (this.ClientSideMediumPassword(pwd)) {
25292             strength = 2;
25293         } else if (this.ClientSideWeakPassword(pwd)) {
25294             strength = 1;
25295         } else {
25296             strength = 0;
25297         }
25298         
25299         Roo.log('strength1: ' + strength);
25300         
25301         //var pm = this.trigger.child('div/div/div').dom;
25302         var pm = this.trigger.child('div/div');
25303         pm.removeClass(this.meterClass);
25304         pm.addClass(this.meterClass[strength]);
25305                 
25306         
25307         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25308                 
25309         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25310         
25311         this._lastPwd = pwd;
25312     },
25313     reset: function ()
25314     {
25315         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25316         
25317         this._lastPwd = '';
25318         
25319         var pm = this.trigger.child('div/div');
25320         pm.removeClass(this.meterClass);
25321         pm.addClass('roo-password-meter-grey');        
25322         
25323         
25324         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25325         
25326         pt.innerHTML = '';
25327         this.inputEl().dom.type='password';
25328     },
25329     // private
25330     validateValue: function (value)
25331     {
25332         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25333             return false;
25334         }
25335         if (value.length == 0) {
25336             if (this.allowBlank) {
25337                 this.clearInvalid();
25338                 return true;
25339             }
25340
25341             this.markInvalid(this.errors.PwdEmpty);
25342             this.errorMsg = this.errors.PwdEmpty;
25343             return false;
25344         }
25345         
25346         if(this.insecure){
25347             return true;
25348         }
25349         
25350         if (!value.match(/[\x21-\x7e]+/)) {
25351             this.markInvalid(this.errors.PwdBadChar);
25352             this.errorMsg = this.errors.PwdBadChar;
25353             return false;
25354         }
25355         if (value.length < 6) {
25356             this.markInvalid(this.errors.PwdShort);
25357             this.errorMsg = this.errors.PwdShort;
25358             return false;
25359         }
25360         if (value.length > 16) {
25361             this.markInvalid(this.errors.PwdLong);
25362             this.errorMsg = this.errors.PwdLong;
25363             return false;
25364         }
25365         var strength;
25366         if (this.ClientSideStrongPassword(value)) {
25367             strength = 3;
25368         } else if (this.ClientSideMediumPassword(value)) {
25369             strength = 2;
25370         } else if (this.ClientSideWeakPassword(value)) {
25371             strength = 1;
25372         } else {
25373             strength = 0;
25374         }
25375
25376         
25377         if (strength < 2) {
25378             //this.markInvalid(this.errors.TooWeak);
25379             this.errorMsg = this.errors.TooWeak;
25380             //return false;
25381         }
25382         
25383         
25384         console.log('strength2: ' + strength);
25385         
25386         //var pm = this.trigger.child('div/div/div').dom;
25387         
25388         var pm = this.trigger.child('div/div');
25389         pm.removeClass(this.meterClass);
25390         pm.addClass(this.meterClass[strength]);
25391                 
25392         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25393                 
25394         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25395         
25396         this.errorMsg = ''; 
25397         return true;
25398     },
25399     // private
25400     CharacterSetChecks: function (type)
25401     {
25402         this.type = type;
25403         this.fResult = false;
25404     },
25405     // private
25406     isctype: function (character, type)
25407     {
25408         switch (type) {  
25409             case this.kCapitalLetter:
25410                 if (character >= 'A' && character <= 'Z') {
25411                     return true;
25412                 }
25413                 break;
25414             
25415             case this.kSmallLetter:
25416                 if (character >= 'a' && character <= 'z') {
25417                     return true;
25418                 }
25419                 break;
25420             
25421             case this.kDigit:
25422                 if (character >= '0' && character <= '9') {
25423                     return true;
25424                 }
25425                 break;
25426             
25427             case this.kPunctuation:
25428                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25429                     return true;
25430                 }
25431                 break;
25432             
25433             default:
25434                 return false;
25435         }
25436
25437     },
25438     // private
25439     IsLongEnough: function (pwd, size)
25440     {
25441         return !(pwd == null || isNaN(size) || pwd.length < size);
25442     },
25443     // private
25444     SpansEnoughCharacterSets: function (word, nb)
25445     {
25446         if (!this.IsLongEnough(word, nb))
25447         {
25448             return false;
25449         }
25450
25451         var characterSetChecks = new Array(
25452             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25453             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25454         );
25455         
25456         for (var index = 0; index < word.length; ++index) {
25457             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25458                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25459                     characterSetChecks[nCharSet].fResult = true;
25460                     break;
25461                 }
25462             }
25463         }
25464
25465         var nCharSets = 0;
25466         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25467             if (characterSetChecks[nCharSet].fResult) {
25468                 ++nCharSets;
25469             }
25470         }
25471
25472         if (nCharSets < nb) {
25473             return false;
25474         }
25475         return true;
25476     },
25477     // private
25478     ClientSideStrongPassword: function (pwd)
25479     {
25480         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25481     },
25482     // private
25483     ClientSideMediumPassword: function (pwd)
25484     {
25485         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25486     },
25487     // private
25488     ClientSideWeakPassword: function (pwd)
25489     {
25490         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25491     }
25492           
25493 })//<script type="text/javascript">
25494
25495 /*
25496  * Based  Ext JS Library 1.1.1
25497  * Copyright(c) 2006-2007, Ext JS, LLC.
25498  * LGPL
25499  *
25500  */
25501  
25502 /**
25503  * @class Roo.HtmlEditorCore
25504  * @extends Roo.Component
25505  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25506  *
25507  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25508  */
25509
25510 Roo.HtmlEditorCore = function(config){
25511     
25512     
25513     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25514     
25515     
25516     this.addEvents({
25517         /**
25518          * @event initialize
25519          * Fires when the editor is fully initialized (including the iframe)
25520          * @param {Roo.HtmlEditorCore} this
25521          */
25522         initialize: true,
25523         /**
25524          * @event activate
25525          * Fires when the editor is first receives the focus. Any insertion must wait
25526          * until after this event.
25527          * @param {Roo.HtmlEditorCore} this
25528          */
25529         activate: true,
25530          /**
25531          * @event beforesync
25532          * Fires before the textarea is updated with content from the editor iframe. Return false
25533          * to cancel the sync.
25534          * @param {Roo.HtmlEditorCore} this
25535          * @param {String} html
25536          */
25537         beforesync: true,
25538          /**
25539          * @event beforepush
25540          * Fires before the iframe editor is updated with content from the textarea. Return false
25541          * to cancel the push.
25542          * @param {Roo.HtmlEditorCore} this
25543          * @param {String} html
25544          */
25545         beforepush: true,
25546          /**
25547          * @event sync
25548          * Fires when the textarea is updated with content from the editor iframe.
25549          * @param {Roo.HtmlEditorCore} this
25550          * @param {String} html
25551          */
25552         sync: true,
25553          /**
25554          * @event push
25555          * Fires when the iframe editor is updated with content from the textarea.
25556          * @param {Roo.HtmlEditorCore} this
25557          * @param {String} html
25558          */
25559         push: true,
25560         
25561         /**
25562          * @event editorevent
25563          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25564          * @param {Roo.HtmlEditorCore} this
25565          */
25566         editorevent: true
25567         
25568     });
25569     
25570     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25571     
25572     // defaults : white / black...
25573     this.applyBlacklists();
25574     
25575     
25576     
25577 };
25578
25579
25580 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25581
25582
25583      /**
25584      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25585      */
25586     
25587     owner : false,
25588     
25589      /**
25590      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25591      *                        Roo.resizable.
25592      */
25593     resizable : false,
25594      /**
25595      * @cfg {Number} height (in pixels)
25596      */   
25597     height: 300,
25598    /**
25599      * @cfg {Number} width (in pixels)
25600      */   
25601     width: 500,
25602     
25603     /**
25604      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25605      * 
25606      */
25607     stylesheets: false,
25608     
25609     // id of frame..
25610     frameId: false,
25611     
25612     // private properties
25613     validationEvent : false,
25614     deferHeight: true,
25615     initialized : false,
25616     activated : false,
25617     sourceEditMode : false,
25618     onFocus : Roo.emptyFn,
25619     iframePad:3,
25620     hideMode:'offsets',
25621     
25622     clearUp: true,
25623     
25624     // blacklist + whitelisted elements..
25625     black: false,
25626     white: false,
25627      
25628     bodyCls : '',
25629
25630     /**
25631      * Protected method that will not generally be called directly. It
25632      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25633      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25634      */
25635     getDocMarkup : function(){
25636         // body styles..
25637         var st = '';
25638         
25639         // inherit styels from page...?? 
25640         if (this.stylesheets === false) {
25641             
25642             Roo.get(document.head).select('style').each(function(node) {
25643                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25644             });
25645             
25646             Roo.get(document.head).select('link').each(function(node) { 
25647                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25648             });
25649             
25650         } else if (!this.stylesheets.length) {
25651                 // simple..
25652                 st = '<style type="text/css">' +
25653                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25654                    '</style>';
25655         } else {
25656             for (var i in this.stylesheets) { 
25657                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25658             }
25659             
25660         }
25661         
25662         st +=  '<style type="text/css">' +
25663             'IMG { cursor: pointer } ' +
25664         '</style>';
25665
25666         var cls = 'roo-htmleditor-body';
25667         
25668         if(this.bodyCls.length){
25669             cls += ' ' + this.bodyCls;
25670         }
25671         
25672         return '<html><head>' + st  +
25673             //<style type="text/css">' +
25674             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25675             //'</style>' +
25676             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25677     },
25678
25679     // private
25680     onRender : function(ct, position)
25681     {
25682         var _t = this;
25683         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25684         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25685         
25686         
25687         this.el.dom.style.border = '0 none';
25688         this.el.dom.setAttribute('tabIndex', -1);
25689         this.el.addClass('x-hidden hide');
25690         
25691         
25692         
25693         if(Roo.isIE){ // fix IE 1px bogus margin
25694             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25695         }
25696        
25697         
25698         this.frameId = Roo.id();
25699         
25700          
25701         
25702         var iframe = this.owner.wrap.createChild({
25703             tag: 'iframe',
25704             cls: 'form-control', // bootstrap..
25705             id: this.frameId,
25706             name: this.frameId,
25707             frameBorder : 'no',
25708             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25709         }, this.el
25710         );
25711         
25712         
25713         this.iframe = iframe.dom;
25714
25715          this.assignDocWin();
25716         
25717         this.doc.designMode = 'on';
25718        
25719         this.doc.open();
25720         this.doc.write(this.getDocMarkup());
25721         this.doc.close();
25722
25723         
25724         var task = { // must defer to wait for browser to be ready
25725             run : function(){
25726                 //console.log("run task?" + this.doc.readyState);
25727                 this.assignDocWin();
25728                 if(this.doc.body || this.doc.readyState == 'complete'){
25729                     try {
25730                         this.doc.designMode="on";
25731                     } catch (e) {
25732                         return;
25733                     }
25734                     Roo.TaskMgr.stop(task);
25735                     this.initEditor.defer(10, this);
25736                 }
25737             },
25738             interval : 10,
25739             duration: 10000,
25740             scope: this
25741         };
25742         Roo.TaskMgr.start(task);
25743
25744     },
25745
25746     // private
25747     onResize : function(w, h)
25748     {
25749          Roo.log('resize: ' +w + ',' + h );
25750         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25751         if(!this.iframe){
25752             return;
25753         }
25754         if(typeof w == 'number'){
25755             
25756             this.iframe.style.width = w + 'px';
25757         }
25758         if(typeof h == 'number'){
25759             
25760             this.iframe.style.height = h + 'px';
25761             if(this.doc){
25762                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25763             }
25764         }
25765         
25766     },
25767
25768     /**
25769      * Toggles the editor between standard and source edit mode.
25770      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25771      */
25772     toggleSourceEdit : function(sourceEditMode){
25773         
25774         this.sourceEditMode = sourceEditMode === true;
25775         
25776         if(this.sourceEditMode){
25777  
25778             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25779             
25780         }else{
25781             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25782             //this.iframe.className = '';
25783             this.deferFocus();
25784         }
25785         //this.setSize(this.owner.wrap.getSize());
25786         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25787     },
25788
25789     
25790   
25791
25792     /**
25793      * Protected method that will not generally be called directly. If you need/want
25794      * custom HTML cleanup, this is the method you should override.
25795      * @param {String} html The HTML to be cleaned
25796      * return {String} The cleaned HTML
25797      */
25798     cleanHtml : function(html){
25799         html = String(html);
25800         if(html.length > 5){
25801             if(Roo.isSafari){ // strip safari nonsense
25802                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25803             }
25804         }
25805         if(html == '&nbsp;'){
25806             html = '';
25807         }
25808         return html;
25809     },
25810
25811     /**
25812      * HTML Editor -> Textarea
25813      * Protected method that will not generally be called directly. Syncs the contents
25814      * of the editor iframe with the textarea.
25815      */
25816     syncValue : function(){
25817         if(this.initialized){
25818             var bd = (this.doc.body || this.doc.documentElement);
25819             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25820             var html = bd.innerHTML;
25821             if(Roo.isSafari){
25822                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25823                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25824                 if(m && m[1]){
25825                     html = '<div style="'+m[0]+'">' + html + '</div>';
25826                 }
25827             }
25828             html = this.cleanHtml(html);
25829             // fix up the special chars.. normaly like back quotes in word...
25830             // however we do not want to do this with chinese..
25831             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25832                 
25833                 var cc = match.charCodeAt();
25834
25835                 // Get the character value, handling surrogate pairs
25836                 if (match.length == 2) {
25837                     // It's a surrogate pair, calculate the Unicode code point
25838                     var high = match.charCodeAt(0) - 0xD800;
25839                     var low  = match.charCodeAt(1) - 0xDC00;
25840                     cc = (high * 0x400) + low + 0x10000;
25841                 }  else if (
25842                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25843                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25844                     (cc >= 0xf900 && cc < 0xfb00 )
25845                 ) {
25846                         return match;
25847                 }  
25848          
25849                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25850                 return "&#" + cc + ";";
25851                 
25852                 
25853             });
25854             
25855             
25856              
25857             if(this.owner.fireEvent('beforesync', this, html) !== false){
25858                 this.el.dom.value = html;
25859                 this.owner.fireEvent('sync', this, html);
25860             }
25861         }
25862     },
25863
25864     /**
25865      * Protected method that will not generally be called directly. Pushes the value of the textarea
25866      * into the iframe editor.
25867      */
25868     pushValue : function(){
25869         if(this.initialized){
25870             var v = this.el.dom.value.trim();
25871             
25872 //            if(v.length < 1){
25873 //                v = '&#160;';
25874 //            }
25875             
25876             if(this.owner.fireEvent('beforepush', this, v) !== false){
25877                 var d = (this.doc.body || this.doc.documentElement);
25878                 d.innerHTML = v;
25879                 this.cleanUpPaste();
25880                 this.el.dom.value = d.innerHTML;
25881                 this.owner.fireEvent('push', this, v);
25882             }
25883         }
25884     },
25885
25886     // private
25887     deferFocus : function(){
25888         this.focus.defer(10, this);
25889     },
25890
25891     // doc'ed in Field
25892     focus : function(){
25893         if(this.win && !this.sourceEditMode){
25894             this.win.focus();
25895         }else{
25896             this.el.focus();
25897         }
25898     },
25899     
25900     assignDocWin: function()
25901     {
25902         var iframe = this.iframe;
25903         
25904          if(Roo.isIE){
25905             this.doc = iframe.contentWindow.document;
25906             this.win = iframe.contentWindow;
25907         } else {
25908 //            if (!Roo.get(this.frameId)) {
25909 //                return;
25910 //            }
25911 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25912 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25913             
25914             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25915                 return;
25916             }
25917             
25918             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25919             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25920         }
25921     },
25922     
25923     // private
25924     initEditor : function(){
25925         //console.log("INIT EDITOR");
25926         this.assignDocWin();
25927         
25928         
25929         
25930         this.doc.designMode="on";
25931         this.doc.open();
25932         this.doc.write(this.getDocMarkup());
25933         this.doc.close();
25934         
25935         var dbody = (this.doc.body || this.doc.documentElement);
25936         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25937         // this copies styles from the containing element into thsi one..
25938         // not sure why we need all of this..
25939         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25940         
25941         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25942         //ss['background-attachment'] = 'fixed'; // w3c
25943         dbody.bgProperties = 'fixed'; // ie
25944         //Roo.DomHelper.applyStyles(dbody, ss);
25945         Roo.EventManager.on(this.doc, {
25946             //'mousedown': this.onEditorEvent,
25947             'mouseup': this.onEditorEvent,
25948             'dblclick': this.onEditorEvent,
25949             'click': this.onEditorEvent,
25950             'keyup': this.onEditorEvent,
25951             buffer:100,
25952             scope: this
25953         });
25954         if(Roo.isGecko){
25955             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25956         }
25957         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25958             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25959         }
25960         this.initialized = true;
25961
25962         this.owner.fireEvent('initialize', this);
25963         this.pushValue();
25964     },
25965
25966     // private
25967     onDestroy : function(){
25968         
25969         
25970         
25971         if(this.rendered){
25972             
25973             //for (var i =0; i < this.toolbars.length;i++) {
25974             //    // fixme - ask toolbars for heights?
25975             //    this.toolbars[i].onDestroy();
25976            // }
25977             
25978             //this.wrap.dom.innerHTML = '';
25979             //this.wrap.remove();
25980         }
25981     },
25982
25983     // private
25984     onFirstFocus : function(){
25985         
25986         this.assignDocWin();
25987         
25988         
25989         this.activated = true;
25990          
25991     
25992         if(Roo.isGecko){ // prevent silly gecko errors
25993             this.win.focus();
25994             var s = this.win.getSelection();
25995             if(!s.focusNode || s.focusNode.nodeType != 3){
25996                 var r = s.getRangeAt(0);
25997                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25998                 r.collapse(true);
25999                 this.deferFocus();
26000             }
26001             try{
26002                 this.execCmd('useCSS', true);
26003                 this.execCmd('styleWithCSS', false);
26004             }catch(e){}
26005         }
26006         this.owner.fireEvent('activate', this);
26007     },
26008
26009     // private
26010     adjustFont: function(btn){
26011         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26012         //if(Roo.isSafari){ // safari
26013         //    adjust *= 2;
26014        // }
26015         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26016         if(Roo.isSafari){ // safari
26017             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26018             v =  (v < 10) ? 10 : v;
26019             v =  (v > 48) ? 48 : v;
26020             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26021             
26022         }
26023         
26024         
26025         v = Math.max(1, v+adjust);
26026         
26027         this.execCmd('FontSize', v  );
26028     },
26029
26030     onEditorEvent : function(e)
26031     {
26032         this.owner.fireEvent('editorevent', this, e);
26033       //  this.updateToolbar();
26034         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26035     },
26036
26037     insertTag : function(tg)
26038     {
26039         // could be a bit smarter... -> wrap the current selected tRoo..
26040         if (tg.toLowerCase() == 'span' ||
26041             tg.toLowerCase() == 'code' ||
26042             tg.toLowerCase() == 'sup' ||
26043             tg.toLowerCase() == 'sub' 
26044             ) {
26045             
26046             range = this.createRange(this.getSelection());
26047             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26048             wrappingNode.appendChild(range.extractContents());
26049             range.insertNode(wrappingNode);
26050
26051             return;
26052             
26053             
26054             
26055         }
26056         this.execCmd("formatblock",   tg);
26057         
26058     },
26059     
26060     insertText : function(txt)
26061     {
26062         
26063         
26064         var range = this.createRange();
26065         range.deleteContents();
26066                //alert(Sender.getAttribute('label'));
26067                
26068         range.insertNode(this.doc.createTextNode(txt));
26069     } ,
26070     
26071      
26072
26073     /**
26074      * Executes a Midas editor command on the editor document and performs necessary focus and
26075      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26076      * @param {String} cmd The Midas command
26077      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26078      */
26079     relayCmd : function(cmd, value){
26080         this.win.focus();
26081         this.execCmd(cmd, value);
26082         this.owner.fireEvent('editorevent', this);
26083         //this.updateToolbar();
26084         this.owner.deferFocus();
26085     },
26086
26087     /**
26088      * Executes a Midas editor command directly on the editor document.
26089      * For visual commands, you should use {@link #relayCmd} instead.
26090      * <b>This should only be called after the editor is initialized.</b>
26091      * @param {String} cmd The Midas command
26092      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26093      */
26094     execCmd : function(cmd, value){
26095         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26096         this.syncValue();
26097     },
26098  
26099  
26100    
26101     /**
26102      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26103      * to insert tRoo.
26104      * @param {String} text | dom node.. 
26105      */
26106     insertAtCursor : function(text)
26107     {
26108         
26109         if(!this.activated){
26110             return;
26111         }
26112         /*
26113         if(Roo.isIE){
26114             this.win.focus();
26115             var r = this.doc.selection.createRange();
26116             if(r){
26117                 r.collapse(true);
26118                 r.pasteHTML(text);
26119                 this.syncValue();
26120                 this.deferFocus();
26121             
26122             }
26123             return;
26124         }
26125         */
26126         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26127             this.win.focus();
26128             
26129             
26130             // from jquery ui (MIT licenced)
26131             var range, node;
26132             var win = this.win;
26133             
26134             if (win.getSelection && win.getSelection().getRangeAt) {
26135                 range = win.getSelection().getRangeAt(0);
26136                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26137                 range.insertNode(node);
26138             } else if (win.document.selection && win.document.selection.createRange) {
26139                 // no firefox support
26140                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26141                 win.document.selection.createRange().pasteHTML(txt);
26142             } else {
26143                 // no firefox support
26144                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26145                 this.execCmd('InsertHTML', txt);
26146             } 
26147             
26148             this.syncValue();
26149             
26150             this.deferFocus();
26151         }
26152     },
26153  // private
26154     mozKeyPress : function(e){
26155         if(e.ctrlKey){
26156             var c = e.getCharCode(), cmd;
26157           
26158             if(c > 0){
26159                 c = String.fromCharCode(c).toLowerCase();
26160                 switch(c){
26161                     case 'b':
26162                         cmd = 'bold';
26163                         break;
26164                     case 'i':
26165                         cmd = 'italic';
26166                         break;
26167                     
26168                     case 'u':
26169                         cmd = 'underline';
26170                         break;
26171                     
26172                     case 'v':
26173                         this.cleanUpPaste.defer(100, this);
26174                         return;
26175                         
26176                 }
26177                 if(cmd){
26178                     this.win.focus();
26179                     this.execCmd(cmd);
26180                     this.deferFocus();
26181                     e.preventDefault();
26182                 }
26183                 
26184             }
26185         }
26186     },
26187
26188     // private
26189     fixKeys : function(){ // load time branching for fastest keydown performance
26190         if(Roo.isIE){
26191             return function(e){
26192                 var k = e.getKey(), r;
26193                 if(k == e.TAB){
26194                     e.stopEvent();
26195                     r = this.doc.selection.createRange();
26196                     if(r){
26197                         r.collapse(true);
26198                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26199                         this.deferFocus();
26200                     }
26201                     return;
26202                 }
26203                 
26204                 if(k == e.ENTER){
26205                     r = this.doc.selection.createRange();
26206                     if(r){
26207                         var target = r.parentElement();
26208                         if(!target || target.tagName.toLowerCase() != 'li'){
26209                             e.stopEvent();
26210                             r.pasteHTML('<br />');
26211                             r.collapse(false);
26212                             r.select();
26213                         }
26214                     }
26215                 }
26216                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26217                     this.cleanUpPaste.defer(100, this);
26218                     return;
26219                 }
26220                 
26221                 
26222             };
26223         }else if(Roo.isOpera){
26224             return function(e){
26225                 var k = e.getKey();
26226                 if(k == e.TAB){
26227                     e.stopEvent();
26228                     this.win.focus();
26229                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26230                     this.deferFocus();
26231                 }
26232                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26233                     this.cleanUpPaste.defer(100, this);
26234                     return;
26235                 }
26236                 
26237             };
26238         }else if(Roo.isSafari){
26239             return function(e){
26240                 var k = e.getKey();
26241                 
26242                 if(k == e.TAB){
26243                     e.stopEvent();
26244                     this.execCmd('InsertText','\t');
26245                     this.deferFocus();
26246                     return;
26247                 }
26248                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26249                     this.cleanUpPaste.defer(100, this);
26250                     return;
26251                 }
26252                 
26253              };
26254         }
26255     }(),
26256     
26257     getAllAncestors: function()
26258     {
26259         var p = this.getSelectedNode();
26260         var a = [];
26261         if (!p) {
26262             a.push(p); // push blank onto stack..
26263             p = this.getParentElement();
26264         }
26265         
26266         
26267         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26268             a.push(p);
26269             p = p.parentNode;
26270         }
26271         a.push(this.doc.body);
26272         return a;
26273     },
26274     lastSel : false,
26275     lastSelNode : false,
26276     
26277     
26278     getSelection : function() 
26279     {
26280         this.assignDocWin();
26281         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26282     },
26283     
26284     getSelectedNode: function() 
26285     {
26286         // this may only work on Gecko!!!
26287         
26288         // should we cache this!!!!
26289         
26290         
26291         
26292          
26293         var range = this.createRange(this.getSelection()).cloneRange();
26294         
26295         if (Roo.isIE) {
26296             var parent = range.parentElement();
26297             while (true) {
26298                 var testRange = range.duplicate();
26299                 testRange.moveToElementText(parent);
26300                 if (testRange.inRange(range)) {
26301                     break;
26302                 }
26303                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26304                     break;
26305                 }
26306                 parent = parent.parentElement;
26307             }
26308             return parent;
26309         }
26310         
26311         // is ancestor a text element.
26312         var ac =  range.commonAncestorContainer;
26313         if (ac.nodeType == 3) {
26314             ac = ac.parentNode;
26315         }
26316         
26317         var ar = ac.childNodes;
26318          
26319         var nodes = [];
26320         var other_nodes = [];
26321         var has_other_nodes = false;
26322         for (var i=0;i<ar.length;i++) {
26323             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26324                 continue;
26325             }
26326             // fullly contained node.
26327             
26328             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26329                 nodes.push(ar[i]);
26330                 continue;
26331             }
26332             
26333             // probably selected..
26334             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26335                 other_nodes.push(ar[i]);
26336                 continue;
26337             }
26338             // outer..
26339             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26340                 continue;
26341             }
26342             
26343             
26344             has_other_nodes = true;
26345         }
26346         if (!nodes.length && other_nodes.length) {
26347             nodes= other_nodes;
26348         }
26349         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26350             return false;
26351         }
26352         
26353         return nodes[0];
26354     },
26355     createRange: function(sel)
26356     {
26357         // this has strange effects when using with 
26358         // top toolbar - not sure if it's a great idea.
26359         //this.editor.contentWindow.focus();
26360         if (typeof sel != "undefined") {
26361             try {
26362                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26363             } catch(e) {
26364                 return this.doc.createRange();
26365             }
26366         } else {
26367             return this.doc.createRange();
26368         }
26369     },
26370     getParentElement: function()
26371     {
26372         
26373         this.assignDocWin();
26374         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26375         
26376         var range = this.createRange(sel);
26377          
26378         try {
26379             var p = range.commonAncestorContainer;
26380             while (p.nodeType == 3) { // text node
26381                 p = p.parentNode;
26382             }
26383             return p;
26384         } catch (e) {
26385             return null;
26386         }
26387     
26388     },
26389     /***
26390      *
26391      * Range intersection.. the hard stuff...
26392      *  '-1' = before
26393      *  '0' = hits..
26394      *  '1' = after.
26395      *         [ -- selected range --- ]
26396      *   [fail]                        [fail]
26397      *
26398      *    basically..
26399      *      if end is before start or  hits it. fail.
26400      *      if start is after end or hits it fail.
26401      *
26402      *   if either hits (but other is outside. - then it's not 
26403      *   
26404      *    
26405      **/
26406     
26407     
26408     // @see http://www.thismuchiknow.co.uk/?p=64.
26409     rangeIntersectsNode : function(range, node)
26410     {
26411         var nodeRange = node.ownerDocument.createRange();
26412         try {
26413             nodeRange.selectNode(node);
26414         } catch (e) {
26415             nodeRange.selectNodeContents(node);
26416         }
26417     
26418         var rangeStartRange = range.cloneRange();
26419         rangeStartRange.collapse(true);
26420     
26421         var rangeEndRange = range.cloneRange();
26422         rangeEndRange.collapse(false);
26423     
26424         var nodeStartRange = nodeRange.cloneRange();
26425         nodeStartRange.collapse(true);
26426     
26427         var nodeEndRange = nodeRange.cloneRange();
26428         nodeEndRange.collapse(false);
26429     
26430         return rangeStartRange.compareBoundaryPoints(
26431                  Range.START_TO_START, nodeEndRange) == -1 &&
26432                rangeEndRange.compareBoundaryPoints(
26433                  Range.START_TO_START, nodeStartRange) == 1;
26434         
26435          
26436     },
26437     rangeCompareNode : function(range, node)
26438     {
26439         var nodeRange = node.ownerDocument.createRange();
26440         try {
26441             nodeRange.selectNode(node);
26442         } catch (e) {
26443             nodeRange.selectNodeContents(node);
26444         }
26445         
26446         
26447         range.collapse(true);
26448     
26449         nodeRange.collapse(true);
26450      
26451         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26452         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26453          
26454         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26455         
26456         var nodeIsBefore   =  ss == 1;
26457         var nodeIsAfter    = ee == -1;
26458         
26459         if (nodeIsBefore && nodeIsAfter) {
26460             return 0; // outer
26461         }
26462         if (!nodeIsBefore && nodeIsAfter) {
26463             return 1; //right trailed.
26464         }
26465         
26466         if (nodeIsBefore && !nodeIsAfter) {
26467             return 2;  // left trailed.
26468         }
26469         // fully contined.
26470         return 3;
26471     },
26472
26473     // private? - in a new class?
26474     cleanUpPaste :  function()
26475     {
26476         // cleans up the whole document..
26477         Roo.log('cleanuppaste');
26478         
26479         this.cleanUpChildren(this.doc.body);
26480         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26481         if (clean != this.doc.body.innerHTML) {
26482             this.doc.body.innerHTML = clean;
26483         }
26484         
26485     },
26486     
26487     cleanWordChars : function(input) {// change the chars to hex code
26488         var he = Roo.HtmlEditorCore;
26489         
26490         var output = input;
26491         Roo.each(he.swapCodes, function(sw) { 
26492             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26493             
26494             output = output.replace(swapper, sw[1]);
26495         });
26496         
26497         return output;
26498     },
26499     
26500     
26501     cleanUpChildren : function (n)
26502     {
26503         if (!n.childNodes.length) {
26504             return;
26505         }
26506         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26507            this.cleanUpChild(n.childNodes[i]);
26508         }
26509     },
26510     
26511     
26512         
26513     
26514     cleanUpChild : function (node)
26515     {
26516         var ed = this;
26517         //console.log(node);
26518         if (node.nodeName == "#text") {
26519             // clean up silly Windows -- stuff?
26520             return; 
26521         }
26522         if (node.nodeName == "#comment") {
26523             node.parentNode.removeChild(node);
26524             // clean up silly Windows -- stuff?
26525             return; 
26526         }
26527         var lcname = node.tagName.toLowerCase();
26528         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26529         // whitelist of tags..
26530         
26531         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26532             // remove node.
26533             node.parentNode.removeChild(node);
26534             return;
26535             
26536         }
26537         
26538         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26539         
26540         // spans with no attributes - just remove them..
26541         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26542             remove_keep_children = true;
26543         }
26544         
26545         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26546         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26547         
26548         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26549         //    remove_keep_children = true;
26550         //}
26551         
26552         if (remove_keep_children) {
26553             this.cleanUpChildren(node);
26554             // inserts everything just before this node...
26555             while (node.childNodes.length) {
26556                 var cn = node.childNodes[0];
26557                 node.removeChild(cn);
26558                 node.parentNode.insertBefore(cn, node);
26559             }
26560             node.parentNode.removeChild(node);
26561             return;
26562         }
26563         
26564         if (!node.attributes || !node.attributes.length) {
26565             
26566           
26567             
26568             
26569             this.cleanUpChildren(node);
26570             return;
26571         }
26572         
26573         function cleanAttr(n,v)
26574         {
26575             
26576             if (v.match(/^\./) || v.match(/^\//)) {
26577                 return;
26578             }
26579             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26580                 return;
26581             }
26582             if (v.match(/^#/)) {
26583                 return;
26584             }
26585             if (v.match(/^\{/)) { // allow template editing.
26586                 return;
26587             }
26588 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26589             node.removeAttribute(n);
26590             
26591         }
26592         
26593         var cwhite = this.cwhite;
26594         var cblack = this.cblack;
26595             
26596         function cleanStyle(n,v)
26597         {
26598             if (v.match(/expression/)) { //XSS?? should we even bother..
26599                 node.removeAttribute(n);
26600                 return;
26601             }
26602             
26603             var parts = v.split(/;/);
26604             var clean = [];
26605             
26606             Roo.each(parts, function(p) {
26607                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26608                 if (!p.length) {
26609                     return true;
26610                 }
26611                 var l = p.split(':').shift().replace(/\s+/g,'');
26612                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26613                 
26614                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26615 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26616                     //node.removeAttribute(n);
26617                     return true;
26618                 }
26619                 //Roo.log()
26620                 // only allow 'c whitelisted system attributes'
26621                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26622 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26623                     //node.removeAttribute(n);
26624                     return true;
26625                 }
26626                 
26627                 
26628                  
26629                 
26630                 clean.push(p);
26631                 return true;
26632             });
26633             if (clean.length) { 
26634                 node.setAttribute(n, clean.join(';'));
26635             } else {
26636                 node.removeAttribute(n);
26637             }
26638             
26639         }
26640         
26641         
26642         for (var i = node.attributes.length-1; i > -1 ; i--) {
26643             var a = node.attributes[i];
26644             //console.log(a);
26645             
26646             if (a.name.toLowerCase().substr(0,2)=='on')  {
26647                 node.removeAttribute(a.name);
26648                 continue;
26649             }
26650             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26651                 node.removeAttribute(a.name);
26652                 continue;
26653             }
26654             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26655                 cleanAttr(a.name,a.value); // fixme..
26656                 continue;
26657             }
26658             if (a.name == 'style') {
26659                 cleanStyle(a.name,a.value);
26660                 continue;
26661             }
26662             /// clean up MS crap..
26663             // tecnically this should be a list of valid class'es..
26664             
26665             
26666             if (a.name == 'class') {
26667                 if (a.value.match(/^Mso/)) {
26668                     node.removeAttribute('class');
26669                 }
26670                 
26671                 if (a.value.match(/^body$/)) {
26672                     node.removeAttribute('class');
26673                 }
26674                 continue;
26675             }
26676             
26677             // style cleanup!?
26678             // class cleanup?
26679             
26680         }
26681         
26682         
26683         this.cleanUpChildren(node);
26684         
26685         
26686     },
26687     
26688     /**
26689      * Clean up MS wordisms...
26690      */
26691     cleanWord : function(node)
26692     {
26693         if (!node) {
26694             this.cleanWord(this.doc.body);
26695             return;
26696         }
26697         
26698         if(
26699                 node.nodeName == 'SPAN' &&
26700                 !node.hasAttributes() &&
26701                 node.childNodes.length == 1 &&
26702                 node.firstChild.nodeName == "#text"  
26703         ) {
26704             var textNode = node.firstChild;
26705             node.removeChild(textNode);
26706             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26707                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26708             }
26709             node.parentNode.insertBefore(textNode, node);
26710             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26711                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26712             }
26713             node.parentNode.removeChild(node);
26714         }
26715         
26716         if (node.nodeName == "#text") {
26717             // clean up silly Windows -- stuff?
26718             return; 
26719         }
26720         if (node.nodeName == "#comment") {
26721             node.parentNode.removeChild(node);
26722             // clean up silly Windows -- stuff?
26723             return; 
26724         }
26725         
26726         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26727             node.parentNode.removeChild(node);
26728             return;
26729         }
26730         //Roo.log(node.tagName);
26731         // remove - but keep children..
26732         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26733             //Roo.log('-- removed');
26734             while (node.childNodes.length) {
26735                 var cn = node.childNodes[0];
26736                 node.removeChild(cn);
26737                 node.parentNode.insertBefore(cn, node);
26738                 // move node to parent - and clean it..
26739                 this.cleanWord(cn);
26740             }
26741             node.parentNode.removeChild(node);
26742             /// no need to iterate chidlren = it's got none..
26743             //this.iterateChildren(node, this.cleanWord);
26744             return;
26745         }
26746         // clean styles
26747         if (node.className.length) {
26748             
26749             var cn = node.className.split(/\W+/);
26750             var cna = [];
26751             Roo.each(cn, function(cls) {
26752                 if (cls.match(/Mso[a-zA-Z]+/)) {
26753                     return;
26754                 }
26755                 cna.push(cls);
26756             });
26757             node.className = cna.length ? cna.join(' ') : '';
26758             if (!cna.length) {
26759                 node.removeAttribute("class");
26760             }
26761         }
26762         
26763         if (node.hasAttribute("lang")) {
26764             node.removeAttribute("lang");
26765         }
26766         
26767         if (node.hasAttribute("style")) {
26768             
26769             var styles = node.getAttribute("style").split(";");
26770             var nstyle = [];
26771             Roo.each(styles, function(s) {
26772                 if (!s.match(/:/)) {
26773                     return;
26774                 }
26775                 var kv = s.split(":");
26776                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26777                     return;
26778                 }
26779                 // what ever is left... we allow.
26780                 nstyle.push(s);
26781             });
26782             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26783             if (!nstyle.length) {
26784                 node.removeAttribute('style');
26785             }
26786         }
26787         this.iterateChildren(node, this.cleanWord);
26788         
26789         
26790         
26791     },
26792     /**
26793      * iterateChildren of a Node, calling fn each time, using this as the scole..
26794      * @param {DomNode} node node to iterate children of.
26795      * @param {Function} fn method of this class to call on each item.
26796      */
26797     iterateChildren : function(node, fn)
26798     {
26799         if (!node.childNodes.length) {
26800                 return;
26801         }
26802         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26803            fn.call(this, node.childNodes[i])
26804         }
26805     },
26806     
26807     
26808     /**
26809      * cleanTableWidths.
26810      *
26811      * Quite often pasting from word etc.. results in tables with column and widths.
26812      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26813      *
26814      */
26815     cleanTableWidths : function(node)
26816     {
26817          
26818          
26819         if (!node) {
26820             this.cleanTableWidths(this.doc.body);
26821             return;
26822         }
26823         
26824         // ignore list...
26825         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26826             return; 
26827         }
26828         Roo.log(node.tagName);
26829         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26830             this.iterateChildren(node, this.cleanTableWidths);
26831             return;
26832         }
26833         if (node.hasAttribute('width')) {
26834             node.removeAttribute('width');
26835         }
26836         
26837          
26838         if (node.hasAttribute("style")) {
26839             // pretty basic...
26840             
26841             var styles = node.getAttribute("style").split(";");
26842             var nstyle = [];
26843             Roo.each(styles, function(s) {
26844                 if (!s.match(/:/)) {
26845                     return;
26846                 }
26847                 var kv = s.split(":");
26848                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26849                     return;
26850                 }
26851                 // what ever is left... we allow.
26852                 nstyle.push(s);
26853             });
26854             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26855             if (!nstyle.length) {
26856                 node.removeAttribute('style');
26857             }
26858         }
26859         
26860         this.iterateChildren(node, this.cleanTableWidths);
26861         
26862         
26863     },
26864     
26865     
26866     
26867     
26868     domToHTML : function(currentElement, depth, nopadtext) {
26869         
26870         depth = depth || 0;
26871         nopadtext = nopadtext || false;
26872     
26873         if (!currentElement) {
26874             return this.domToHTML(this.doc.body);
26875         }
26876         
26877         //Roo.log(currentElement);
26878         var j;
26879         var allText = false;
26880         var nodeName = currentElement.nodeName;
26881         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26882         
26883         if  (nodeName == '#text') {
26884             
26885             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26886         }
26887         
26888         
26889         var ret = '';
26890         if (nodeName != 'BODY') {
26891              
26892             var i = 0;
26893             // Prints the node tagName, such as <A>, <IMG>, etc
26894             if (tagName) {
26895                 var attr = [];
26896                 for(i = 0; i < currentElement.attributes.length;i++) {
26897                     // quoting?
26898                     var aname = currentElement.attributes.item(i).name;
26899                     if (!currentElement.attributes.item(i).value.length) {
26900                         continue;
26901                     }
26902                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26903                 }
26904                 
26905                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26906             } 
26907             else {
26908                 
26909                 // eack
26910             }
26911         } else {
26912             tagName = false;
26913         }
26914         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26915             return ret;
26916         }
26917         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26918             nopadtext = true;
26919         }
26920         
26921         
26922         // Traverse the tree
26923         i = 0;
26924         var currentElementChild = currentElement.childNodes.item(i);
26925         var allText = true;
26926         var innerHTML  = '';
26927         lastnode = '';
26928         while (currentElementChild) {
26929             // Formatting code (indent the tree so it looks nice on the screen)
26930             var nopad = nopadtext;
26931             if (lastnode == 'SPAN') {
26932                 nopad  = true;
26933             }
26934             // text
26935             if  (currentElementChild.nodeName == '#text') {
26936                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26937                 toadd = nopadtext ? toadd : toadd.trim();
26938                 if (!nopad && toadd.length > 80) {
26939                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26940                 }
26941                 innerHTML  += toadd;
26942                 
26943                 i++;
26944                 currentElementChild = currentElement.childNodes.item(i);
26945                 lastNode = '';
26946                 continue;
26947             }
26948             allText = false;
26949             
26950             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26951                 
26952             // Recursively traverse the tree structure of the child node
26953             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26954             lastnode = currentElementChild.nodeName;
26955             i++;
26956             currentElementChild=currentElement.childNodes.item(i);
26957         }
26958         
26959         ret += innerHTML;
26960         
26961         if (!allText) {
26962                 // The remaining code is mostly for formatting the tree
26963             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26964         }
26965         
26966         
26967         if (tagName) {
26968             ret+= "</"+tagName+">";
26969         }
26970         return ret;
26971         
26972     },
26973         
26974     applyBlacklists : function()
26975     {
26976         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26977         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26978         
26979         this.white = [];
26980         this.black = [];
26981         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26982             if (b.indexOf(tag) > -1) {
26983                 return;
26984             }
26985             this.white.push(tag);
26986             
26987         }, this);
26988         
26989         Roo.each(w, function(tag) {
26990             if (b.indexOf(tag) > -1) {
26991                 return;
26992             }
26993             if (this.white.indexOf(tag) > -1) {
26994                 return;
26995             }
26996             this.white.push(tag);
26997             
26998         }, this);
26999         
27000         
27001         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27002             if (w.indexOf(tag) > -1) {
27003                 return;
27004             }
27005             this.black.push(tag);
27006             
27007         }, this);
27008         
27009         Roo.each(b, function(tag) {
27010             if (w.indexOf(tag) > -1) {
27011                 return;
27012             }
27013             if (this.black.indexOf(tag) > -1) {
27014                 return;
27015             }
27016             this.black.push(tag);
27017             
27018         }, this);
27019         
27020         
27021         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27022         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27023         
27024         this.cwhite = [];
27025         this.cblack = [];
27026         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27027             if (b.indexOf(tag) > -1) {
27028                 return;
27029             }
27030             this.cwhite.push(tag);
27031             
27032         }, this);
27033         
27034         Roo.each(w, function(tag) {
27035             if (b.indexOf(tag) > -1) {
27036                 return;
27037             }
27038             if (this.cwhite.indexOf(tag) > -1) {
27039                 return;
27040             }
27041             this.cwhite.push(tag);
27042             
27043         }, this);
27044         
27045         
27046         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27047             if (w.indexOf(tag) > -1) {
27048                 return;
27049             }
27050             this.cblack.push(tag);
27051             
27052         }, this);
27053         
27054         Roo.each(b, function(tag) {
27055             if (w.indexOf(tag) > -1) {
27056                 return;
27057             }
27058             if (this.cblack.indexOf(tag) > -1) {
27059                 return;
27060             }
27061             this.cblack.push(tag);
27062             
27063         }, this);
27064     },
27065     
27066     setStylesheets : function(stylesheets)
27067     {
27068         if(typeof(stylesheets) == 'string'){
27069             Roo.get(this.iframe.contentDocument.head).createChild({
27070                 tag : 'link',
27071                 rel : 'stylesheet',
27072                 type : 'text/css',
27073                 href : stylesheets
27074             });
27075             
27076             return;
27077         }
27078         var _this = this;
27079      
27080         Roo.each(stylesheets, function(s) {
27081             if(!s.length){
27082                 return;
27083             }
27084             
27085             Roo.get(_this.iframe.contentDocument.head).createChild({
27086                 tag : 'link',
27087                 rel : 'stylesheet',
27088                 type : 'text/css',
27089                 href : s
27090             });
27091         });
27092
27093         
27094     },
27095     
27096     removeStylesheets : function()
27097     {
27098         var _this = this;
27099         
27100         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27101             s.remove();
27102         });
27103     },
27104     
27105     setStyle : function(style)
27106     {
27107         Roo.get(this.iframe.contentDocument.head).createChild({
27108             tag : 'style',
27109             type : 'text/css',
27110             html : style
27111         });
27112
27113         return;
27114     }
27115     
27116     // hide stuff that is not compatible
27117     /**
27118      * @event blur
27119      * @hide
27120      */
27121     /**
27122      * @event change
27123      * @hide
27124      */
27125     /**
27126      * @event focus
27127      * @hide
27128      */
27129     /**
27130      * @event specialkey
27131      * @hide
27132      */
27133     /**
27134      * @cfg {String} fieldClass @hide
27135      */
27136     /**
27137      * @cfg {String} focusClass @hide
27138      */
27139     /**
27140      * @cfg {String} autoCreate @hide
27141      */
27142     /**
27143      * @cfg {String} inputType @hide
27144      */
27145     /**
27146      * @cfg {String} invalidClass @hide
27147      */
27148     /**
27149      * @cfg {String} invalidText @hide
27150      */
27151     /**
27152      * @cfg {String} msgFx @hide
27153      */
27154     /**
27155      * @cfg {String} validateOnBlur @hide
27156      */
27157 });
27158
27159 Roo.HtmlEditorCore.white = [
27160         'area', 'br', 'img', 'input', 'hr', 'wbr',
27161         
27162        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27163        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27164        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27165        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27166        'table',   'ul',         'xmp', 
27167        
27168        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27169       'thead',   'tr', 
27170      
27171       'dir', 'menu', 'ol', 'ul', 'dl',
27172        
27173       'embed',  'object'
27174 ];
27175
27176
27177 Roo.HtmlEditorCore.black = [
27178     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27179         'applet', // 
27180         'base',   'basefont', 'bgsound', 'blink',  'body', 
27181         'frame',  'frameset', 'head',    'html',   'ilayer', 
27182         'iframe', 'layer',  'link',     'meta',    'object',   
27183         'script', 'style' ,'title',  'xml' // clean later..
27184 ];
27185 Roo.HtmlEditorCore.clean = [
27186     'script', 'style', 'title', 'xml'
27187 ];
27188 Roo.HtmlEditorCore.remove = [
27189     'font'
27190 ];
27191 // attributes..
27192
27193 Roo.HtmlEditorCore.ablack = [
27194     'on'
27195 ];
27196     
27197 Roo.HtmlEditorCore.aclean = [ 
27198     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27199 ];
27200
27201 // protocols..
27202 Roo.HtmlEditorCore.pwhite= [
27203         'http',  'https',  'mailto'
27204 ];
27205
27206 // white listed style attributes.
27207 Roo.HtmlEditorCore.cwhite= [
27208       //  'text-align', /// default is to allow most things..
27209       
27210          
27211 //        'font-size'//??
27212 ];
27213
27214 // black listed style attributes.
27215 Roo.HtmlEditorCore.cblack= [
27216       //  'font-size' -- this can be set by the project 
27217 ];
27218
27219
27220 Roo.HtmlEditorCore.swapCodes   =[ 
27221     [    8211, "&#8211;" ], 
27222     [    8212, "&#8212;" ], 
27223     [    8216,  "'" ],  
27224     [    8217, "'" ],  
27225     [    8220, '"' ],  
27226     [    8221, '"' ],  
27227     [    8226, "*" ],  
27228     [    8230, "..." ]
27229 ]; 
27230
27231     /*
27232  * - LGPL
27233  *
27234  * HtmlEditor
27235  * 
27236  */
27237
27238 /**
27239  * @class Roo.bootstrap.HtmlEditor
27240  * @extends Roo.bootstrap.TextArea
27241  * Bootstrap HtmlEditor class
27242
27243  * @constructor
27244  * Create a new HtmlEditor
27245  * @param {Object} config The config object
27246  */
27247
27248 Roo.bootstrap.HtmlEditor = function(config){
27249     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27250     if (!this.toolbars) {
27251         this.toolbars = [];
27252     }
27253     
27254     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27255     this.addEvents({
27256             /**
27257              * @event initialize
27258              * Fires when the editor is fully initialized (including the iframe)
27259              * @param {HtmlEditor} this
27260              */
27261             initialize: true,
27262             /**
27263              * @event activate
27264              * Fires when the editor is first receives the focus. Any insertion must wait
27265              * until after this event.
27266              * @param {HtmlEditor} this
27267              */
27268             activate: true,
27269              /**
27270              * @event beforesync
27271              * Fires before the textarea is updated with content from the editor iframe. Return false
27272              * to cancel the sync.
27273              * @param {HtmlEditor} this
27274              * @param {String} html
27275              */
27276             beforesync: true,
27277              /**
27278              * @event beforepush
27279              * Fires before the iframe editor is updated with content from the textarea. Return false
27280              * to cancel the push.
27281              * @param {HtmlEditor} this
27282              * @param {String} html
27283              */
27284             beforepush: true,
27285              /**
27286              * @event sync
27287              * Fires when the textarea is updated with content from the editor iframe.
27288              * @param {HtmlEditor} this
27289              * @param {String} html
27290              */
27291             sync: true,
27292              /**
27293              * @event push
27294              * Fires when the iframe editor is updated with content from the textarea.
27295              * @param {HtmlEditor} this
27296              * @param {String} html
27297              */
27298             push: true,
27299              /**
27300              * @event editmodechange
27301              * Fires when the editor switches edit modes
27302              * @param {HtmlEditor} this
27303              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27304              */
27305             editmodechange: true,
27306             /**
27307              * @event editorevent
27308              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27309              * @param {HtmlEditor} this
27310              */
27311             editorevent: true,
27312             /**
27313              * @event firstfocus
27314              * Fires when on first focus - needed by toolbars..
27315              * @param {HtmlEditor} this
27316              */
27317             firstfocus: true,
27318             /**
27319              * @event autosave
27320              * Auto save the htmlEditor value as a file into Events
27321              * @param {HtmlEditor} this
27322              */
27323             autosave: true,
27324             /**
27325              * @event savedpreview
27326              * preview the saved version of htmlEditor
27327              * @param {HtmlEditor} this
27328              */
27329             savedpreview: true
27330         });
27331 };
27332
27333
27334 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27335     
27336     
27337       /**
27338      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27339      */
27340     toolbars : false,
27341     
27342      /**
27343     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27344     */
27345     btns : [],
27346    
27347      /**
27348      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27349      *                        Roo.resizable.
27350      */
27351     resizable : false,
27352      /**
27353      * @cfg {Number} height (in pixels)
27354      */   
27355     height: 300,
27356    /**
27357      * @cfg {Number} width (in pixels)
27358      */   
27359     width: false,
27360     
27361     /**
27362      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27363      * 
27364      */
27365     stylesheets: false,
27366     
27367     // id of frame..
27368     frameId: false,
27369     
27370     // private properties
27371     validationEvent : false,
27372     deferHeight: true,
27373     initialized : false,
27374     activated : false,
27375     
27376     onFocus : Roo.emptyFn,
27377     iframePad:3,
27378     hideMode:'offsets',
27379     
27380     tbContainer : false,
27381     
27382     bodyCls : '',
27383     
27384     toolbarContainer :function() {
27385         return this.wrap.select('.x-html-editor-tb',true).first();
27386     },
27387
27388     /**
27389      * Protected method that will not generally be called directly. It
27390      * is called when the editor creates its toolbar. Override this method if you need to
27391      * add custom toolbar buttons.
27392      * @param {HtmlEditor} editor
27393      */
27394     createToolbar : function(){
27395         Roo.log('renewing');
27396         Roo.log("create toolbars");
27397         
27398         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27399         this.toolbars[0].render(this.toolbarContainer());
27400         
27401         return;
27402         
27403 //        if (!editor.toolbars || !editor.toolbars.length) {
27404 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27405 //        }
27406 //        
27407 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27408 //            editor.toolbars[i] = Roo.factory(
27409 //                    typeof(editor.toolbars[i]) == 'string' ?
27410 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27411 //                Roo.bootstrap.HtmlEditor);
27412 //            editor.toolbars[i].init(editor);
27413 //        }
27414     },
27415
27416      
27417     // private
27418     onRender : function(ct, position)
27419     {
27420        // Roo.log("Call onRender: " + this.xtype);
27421         var _t = this;
27422         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27423       
27424         this.wrap = this.inputEl().wrap({
27425             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27426         });
27427         
27428         this.editorcore.onRender(ct, position);
27429          
27430         if (this.resizable) {
27431             this.resizeEl = new Roo.Resizable(this.wrap, {
27432                 pinned : true,
27433                 wrap: true,
27434                 dynamic : true,
27435                 minHeight : this.height,
27436                 height: this.height,
27437                 handles : this.resizable,
27438                 width: this.width,
27439                 listeners : {
27440                     resize : function(r, w, h) {
27441                         _t.onResize(w,h); // -something
27442                     }
27443                 }
27444             });
27445             
27446         }
27447         this.createToolbar(this);
27448        
27449         
27450         if(!this.width && this.resizable){
27451             this.setSize(this.wrap.getSize());
27452         }
27453         if (this.resizeEl) {
27454             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27455             // should trigger onReize..
27456         }
27457         
27458     },
27459
27460     // private
27461     onResize : function(w, h)
27462     {
27463         Roo.log('resize: ' +w + ',' + h );
27464         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27465         var ew = false;
27466         var eh = false;
27467         
27468         if(this.inputEl() ){
27469             if(typeof w == 'number'){
27470                 var aw = w - this.wrap.getFrameWidth('lr');
27471                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27472                 ew = aw;
27473             }
27474             if(typeof h == 'number'){
27475                  var tbh = -11;  // fixme it needs to tool bar size!
27476                 for (var i =0; i < this.toolbars.length;i++) {
27477                     // fixme - ask toolbars for heights?
27478                     tbh += this.toolbars[i].el.getHeight();
27479                     //if (this.toolbars[i].footer) {
27480                     //    tbh += this.toolbars[i].footer.el.getHeight();
27481                     //}
27482                 }
27483               
27484                 
27485                 
27486                 
27487                 
27488                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27489                 ah -= 5; // knock a few pixes off for look..
27490                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27491                 var eh = ah;
27492             }
27493         }
27494         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27495         this.editorcore.onResize(ew,eh);
27496         
27497     },
27498
27499     /**
27500      * Toggles the editor between standard and source edit mode.
27501      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27502      */
27503     toggleSourceEdit : function(sourceEditMode)
27504     {
27505         this.editorcore.toggleSourceEdit(sourceEditMode);
27506         
27507         if(this.editorcore.sourceEditMode){
27508             Roo.log('editor - showing textarea');
27509             
27510 //            Roo.log('in');
27511 //            Roo.log(this.syncValue());
27512             this.syncValue();
27513             this.inputEl().removeClass(['hide', 'x-hidden']);
27514             this.inputEl().dom.removeAttribute('tabIndex');
27515             this.inputEl().focus();
27516         }else{
27517             Roo.log('editor - hiding textarea');
27518 //            Roo.log('out')
27519 //            Roo.log(this.pushValue()); 
27520             this.pushValue();
27521             
27522             this.inputEl().addClass(['hide', 'x-hidden']);
27523             this.inputEl().dom.setAttribute('tabIndex', -1);
27524             //this.deferFocus();
27525         }
27526          
27527         if(this.resizable){
27528             this.setSize(this.wrap.getSize());
27529         }
27530         
27531         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27532     },
27533  
27534     // private (for BoxComponent)
27535     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27536
27537     // private (for BoxComponent)
27538     getResizeEl : function(){
27539         return this.wrap;
27540     },
27541
27542     // private (for BoxComponent)
27543     getPositionEl : function(){
27544         return this.wrap;
27545     },
27546
27547     // private
27548     initEvents : function(){
27549         this.originalValue = this.getValue();
27550     },
27551
27552 //    /**
27553 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27554 //     * @method
27555 //     */
27556 //    markInvalid : Roo.emptyFn,
27557 //    /**
27558 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27559 //     * @method
27560 //     */
27561 //    clearInvalid : Roo.emptyFn,
27562
27563     setValue : function(v){
27564         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27565         this.editorcore.pushValue();
27566     },
27567
27568      
27569     // private
27570     deferFocus : function(){
27571         this.focus.defer(10, this);
27572     },
27573
27574     // doc'ed in Field
27575     focus : function(){
27576         this.editorcore.focus();
27577         
27578     },
27579       
27580
27581     // private
27582     onDestroy : function(){
27583         
27584         
27585         
27586         if(this.rendered){
27587             
27588             for (var i =0; i < this.toolbars.length;i++) {
27589                 // fixme - ask toolbars for heights?
27590                 this.toolbars[i].onDestroy();
27591             }
27592             
27593             this.wrap.dom.innerHTML = '';
27594             this.wrap.remove();
27595         }
27596     },
27597
27598     // private
27599     onFirstFocus : function(){
27600         //Roo.log("onFirstFocus");
27601         this.editorcore.onFirstFocus();
27602          for (var i =0; i < this.toolbars.length;i++) {
27603             this.toolbars[i].onFirstFocus();
27604         }
27605         
27606     },
27607     
27608     // private
27609     syncValue : function()
27610     {   
27611         this.editorcore.syncValue();
27612     },
27613     
27614     pushValue : function()
27615     {   
27616         this.editorcore.pushValue();
27617     }
27618      
27619     
27620     // hide stuff that is not compatible
27621     /**
27622      * @event blur
27623      * @hide
27624      */
27625     /**
27626      * @event change
27627      * @hide
27628      */
27629     /**
27630      * @event focus
27631      * @hide
27632      */
27633     /**
27634      * @event specialkey
27635      * @hide
27636      */
27637     /**
27638      * @cfg {String} fieldClass @hide
27639      */
27640     /**
27641      * @cfg {String} focusClass @hide
27642      */
27643     /**
27644      * @cfg {String} autoCreate @hide
27645      */
27646     /**
27647      * @cfg {String} inputType @hide
27648      */
27649      
27650     /**
27651      * @cfg {String} invalidText @hide
27652      */
27653     /**
27654      * @cfg {String} msgFx @hide
27655      */
27656     /**
27657      * @cfg {String} validateOnBlur @hide
27658      */
27659 });
27660  
27661     
27662    
27663    
27664    
27665       
27666 Roo.namespace('Roo.bootstrap.htmleditor');
27667 /**
27668  * @class Roo.bootstrap.HtmlEditorToolbar1
27669  * Basic Toolbar
27670  * 
27671  * @example
27672  * Usage:
27673  *
27674  new Roo.bootstrap.HtmlEditor({
27675     ....
27676     toolbars : [
27677         new Roo.bootstrap.HtmlEditorToolbar1({
27678             disable : { fonts: 1 , format: 1, ..., ... , ...],
27679             btns : [ .... ]
27680         })
27681     }
27682      
27683  * 
27684  * @cfg {Object} disable List of elements to disable..
27685  * @cfg {Array} btns List of additional buttons.
27686  * 
27687  * 
27688  * NEEDS Extra CSS? 
27689  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27690  */
27691  
27692 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27693 {
27694     
27695     Roo.apply(this, config);
27696     
27697     // default disabled, based on 'good practice'..
27698     this.disable = this.disable || {};
27699     Roo.applyIf(this.disable, {
27700         fontSize : true,
27701         colors : true,
27702         specialElements : true
27703     });
27704     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27705     
27706     this.editor = config.editor;
27707     this.editorcore = config.editor.editorcore;
27708     
27709     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27710     
27711     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27712     // dont call parent... till later.
27713 }
27714 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27715      
27716     bar : true,
27717     
27718     editor : false,
27719     editorcore : false,
27720     
27721     
27722     formats : [
27723         "p" ,  
27724         "h1","h2","h3","h4","h5","h6", 
27725         "pre", "code", 
27726         "abbr", "acronym", "address", "cite", "samp", "var",
27727         'div','span'
27728     ],
27729     
27730     onRender : function(ct, position)
27731     {
27732        // Roo.log("Call onRender: " + this.xtype);
27733         
27734        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27735        Roo.log(this.el);
27736        this.el.dom.style.marginBottom = '0';
27737        var _this = this;
27738        var editorcore = this.editorcore;
27739        var editor= this.editor;
27740        
27741        var children = [];
27742        var btn = function(id,cmd , toggle, handler, html){
27743        
27744             var  event = toggle ? 'toggle' : 'click';
27745        
27746             var a = {
27747                 size : 'sm',
27748                 xtype: 'Button',
27749                 xns: Roo.bootstrap,
27750                 //glyphicon : id,
27751                 fa: id,
27752                 cmd : id || cmd,
27753                 enableToggle:toggle !== false,
27754                 html : html || '',
27755                 pressed : toggle ? false : null,
27756                 listeners : {}
27757             };
27758             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27759                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27760             };
27761             children.push(a);
27762             return a;
27763        }
27764        
27765     //    var cb_box = function...
27766         
27767         var style = {
27768                 xtype: 'Button',
27769                 size : 'sm',
27770                 xns: Roo.bootstrap,
27771                 fa : 'font',
27772                 //html : 'submit'
27773                 menu : {
27774                     xtype: 'Menu',
27775                     xns: Roo.bootstrap,
27776                     items:  []
27777                 }
27778         };
27779         Roo.each(this.formats, function(f) {
27780             style.menu.items.push({
27781                 xtype :'MenuItem',
27782                 xns: Roo.bootstrap,
27783                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27784                 tagname : f,
27785                 listeners : {
27786                     click : function()
27787                     {
27788                         editorcore.insertTag(this.tagname);
27789                         editor.focus();
27790                     }
27791                 }
27792                 
27793             });
27794         });
27795         children.push(style);   
27796         
27797         btn('bold',false,true);
27798         btn('italic',false,true);
27799         btn('align-left', 'justifyleft',true);
27800         btn('align-center', 'justifycenter',true);
27801         btn('align-right' , 'justifyright',true);
27802         btn('link', false, false, function(btn) {
27803             //Roo.log("create link?");
27804             var url = prompt(this.createLinkText, this.defaultLinkValue);
27805             if(url && url != 'http:/'+'/'){
27806                 this.editorcore.relayCmd('createlink', url);
27807             }
27808         }),
27809         btn('list','insertunorderedlist',true);
27810         btn('pencil', false,true, function(btn){
27811                 Roo.log(this);
27812                 this.toggleSourceEdit(btn.pressed);
27813         });
27814         
27815         if (this.editor.btns.length > 0) {
27816             for (var i = 0; i<this.editor.btns.length; i++) {
27817                 children.push(this.editor.btns[i]);
27818             }
27819         }
27820         
27821         /*
27822         var cog = {
27823                 xtype: 'Button',
27824                 size : 'sm',
27825                 xns: Roo.bootstrap,
27826                 glyphicon : 'cog',
27827                 //html : 'submit'
27828                 menu : {
27829                     xtype: 'Menu',
27830                     xns: Roo.bootstrap,
27831                     items:  []
27832                 }
27833         };
27834         
27835         cog.menu.items.push({
27836             xtype :'MenuItem',
27837             xns: Roo.bootstrap,
27838             html : Clean styles,
27839             tagname : f,
27840             listeners : {
27841                 click : function()
27842                 {
27843                     editorcore.insertTag(this.tagname);
27844                     editor.focus();
27845                 }
27846             }
27847             
27848         });
27849        */
27850         
27851          
27852        this.xtype = 'NavSimplebar';
27853         
27854         for(var i=0;i< children.length;i++) {
27855             
27856             this.buttons.add(this.addxtypeChild(children[i]));
27857             
27858         }
27859         
27860         editor.on('editorevent', this.updateToolbar, this);
27861     },
27862     onBtnClick : function(id)
27863     {
27864        this.editorcore.relayCmd(id);
27865        this.editorcore.focus();
27866     },
27867     
27868     /**
27869      * Protected method that will not generally be called directly. It triggers
27870      * a toolbar update by reading the markup state of the current selection in the editor.
27871      */
27872     updateToolbar: function(){
27873
27874         if(!this.editorcore.activated){
27875             this.editor.onFirstFocus(); // is this neeed?
27876             return;
27877         }
27878
27879         var btns = this.buttons; 
27880         var doc = this.editorcore.doc;
27881         btns.get('bold').setActive(doc.queryCommandState('bold'));
27882         btns.get('italic').setActive(doc.queryCommandState('italic'));
27883         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27884         
27885         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27886         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27887         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27888         
27889         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27890         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27891          /*
27892         
27893         var ans = this.editorcore.getAllAncestors();
27894         if (this.formatCombo) {
27895             
27896             
27897             var store = this.formatCombo.store;
27898             this.formatCombo.setValue("");
27899             for (var i =0; i < ans.length;i++) {
27900                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27901                     // select it..
27902                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27903                     break;
27904                 }
27905             }
27906         }
27907         
27908         
27909         
27910         // hides menus... - so this cant be on a menu...
27911         Roo.bootstrap.MenuMgr.hideAll();
27912         */
27913         Roo.bootstrap.MenuMgr.hideAll();
27914         //this.editorsyncValue();
27915     },
27916     onFirstFocus: function() {
27917         this.buttons.each(function(item){
27918            item.enable();
27919         });
27920     },
27921     toggleSourceEdit : function(sourceEditMode){
27922         
27923           
27924         if(sourceEditMode){
27925             Roo.log("disabling buttons");
27926            this.buttons.each( function(item){
27927                 if(item.cmd != 'pencil'){
27928                     item.disable();
27929                 }
27930             });
27931           
27932         }else{
27933             Roo.log("enabling buttons");
27934             if(this.editorcore.initialized){
27935                 this.buttons.each( function(item){
27936                     item.enable();
27937                 });
27938             }
27939             
27940         }
27941         Roo.log("calling toggole on editor");
27942         // tell the editor that it's been pressed..
27943         this.editor.toggleSourceEdit(sourceEditMode);
27944        
27945     }
27946 });
27947
27948
27949
27950
27951  
27952 /*
27953  * - LGPL
27954  */
27955
27956 /**
27957  * @class Roo.bootstrap.Markdown
27958  * @extends Roo.bootstrap.TextArea
27959  * Bootstrap Showdown editable area
27960  * @cfg {string} content
27961  * 
27962  * @constructor
27963  * Create a new Showdown
27964  */
27965
27966 Roo.bootstrap.Markdown = function(config){
27967     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27968    
27969 };
27970
27971 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27972     
27973     editing :false,
27974     
27975     initEvents : function()
27976     {
27977         
27978         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27979         this.markdownEl = this.el.createChild({
27980             cls : 'roo-markdown-area'
27981         });
27982         this.inputEl().addClass('d-none');
27983         if (this.getValue() == '') {
27984             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27985             
27986         } else {
27987             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27988         }
27989         this.markdownEl.on('click', this.toggleTextEdit, this);
27990         this.on('blur', this.toggleTextEdit, this);
27991         this.on('specialkey', this.resizeTextArea, this);
27992     },
27993     
27994     toggleTextEdit : function()
27995     {
27996         var sh = this.markdownEl.getHeight();
27997         this.inputEl().addClass('d-none');
27998         this.markdownEl.addClass('d-none');
27999         if (!this.editing) {
28000             // show editor?
28001             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28002             this.inputEl().removeClass('d-none');
28003             this.inputEl().focus();
28004             this.editing = true;
28005             return;
28006         }
28007         // show showdown...
28008         this.updateMarkdown();
28009         this.markdownEl.removeClass('d-none');
28010         this.editing = false;
28011         return;
28012     },
28013     updateMarkdown : function()
28014     {
28015         if (this.getValue() == '') {
28016             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28017             return;
28018         }
28019  
28020         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28021     },
28022     
28023     resizeTextArea: function () {
28024         
28025         var sh = 100;
28026         Roo.log([sh, this.getValue().split("\n").length * 30]);
28027         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28028     },
28029     setValue : function(val)
28030     {
28031         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28032         if (!this.editing) {
28033             this.updateMarkdown();
28034         }
28035         
28036     },
28037     focus : function()
28038     {
28039         if (!this.editing) {
28040             this.toggleTextEdit();
28041         }
28042         
28043     }
28044
28045
28046 });/*
28047  * Based on:
28048  * Ext JS Library 1.1.1
28049  * Copyright(c) 2006-2007, Ext JS, LLC.
28050  *
28051  * Originally Released Under LGPL - original licence link has changed is not relivant.
28052  *
28053  * Fork - LGPL
28054  * <script type="text/javascript">
28055  */
28056  
28057 /**
28058  * @class Roo.bootstrap.PagingToolbar
28059  * @extends Roo.bootstrap.NavSimplebar
28060  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28061  * @constructor
28062  * Create a new PagingToolbar
28063  * @param {Object} config The config object
28064  * @param {Roo.data.Store} store
28065  */
28066 Roo.bootstrap.PagingToolbar = function(config)
28067 {
28068     // old args format still supported... - xtype is prefered..
28069         // created from xtype...
28070     
28071     this.ds = config.dataSource;
28072     
28073     if (config.store && !this.ds) {
28074         this.store= Roo.factory(config.store, Roo.data);
28075         this.ds = this.store;
28076         this.ds.xmodule = this.xmodule || false;
28077     }
28078     
28079     this.toolbarItems = [];
28080     if (config.items) {
28081         this.toolbarItems = config.items;
28082     }
28083     
28084     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28085     
28086     this.cursor = 0;
28087     
28088     if (this.ds) { 
28089         this.bind(this.ds);
28090     }
28091     
28092     if (Roo.bootstrap.version == 4) {
28093         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28094     } else {
28095         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28096     }
28097     
28098 };
28099
28100 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28101     /**
28102      * @cfg {Roo.data.Store} dataSource
28103      * The underlying data store providing the paged data
28104      */
28105     /**
28106      * @cfg {String/HTMLElement/Element} container
28107      * container The id or element that will contain the toolbar
28108      */
28109     /**
28110      * @cfg {Boolean} displayInfo
28111      * True to display the displayMsg (defaults to false)
28112      */
28113     /**
28114      * @cfg {Number} pageSize
28115      * The number of records to display per page (defaults to 20)
28116      */
28117     pageSize: 20,
28118     /**
28119      * @cfg {String} displayMsg
28120      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28121      */
28122     displayMsg : 'Displaying {0} - {1} of {2}',
28123     /**
28124      * @cfg {String} emptyMsg
28125      * The message to display when no records are found (defaults to "No data to display")
28126      */
28127     emptyMsg : 'No data to display',
28128     /**
28129      * Customizable piece of the default paging text (defaults to "Page")
28130      * @type String
28131      */
28132     beforePageText : "Page",
28133     /**
28134      * Customizable piece of the default paging text (defaults to "of %0")
28135      * @type String
28136      */
28137     afterPageText : "of {0}",
28138     /**
28139      * Customizable piece of the default paging text (defaults to "First Page")
28140      * @type String
28141      */
28142     firstText : "First Page",
28143     /**
28144      * Customizable piece of the default paging text (defaults to "Previous Page")
28145      * @type String
28146      */
28147     prevText : "Previous Page",
28148     /**
28149      * Customizable piece of the default paging text (defaults to "Next Page")
28150      * @type String
28151      */
28152     nextText : "Next Page",
28153     /**
28154      * Customizable piece of the default paging text (defaults to "Last Page")
28155      * @type String
28156      */
28157     lastText : "Last Page",
28158     /**
28159      * Customizable piece of the default paging text (defaults to "Refresh")
28160      * @type String
28161      */
28162     refreshText : "Refresh",
28163
28164     buttons : false,
28165     // private
28166     onRender : function(ct, position) 
28167     {
28168         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28169         this.navgroup.parentId = this.id;
28170         this.navgroup.onRender(this.el, null);
28171         // add the buttons to the navgroup
28172         
28173         if(this.displayInfo){
28174             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28175             this.displayEl = this.el.select('.x-paging-info', true).first();
28176 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28177 //            this.displayEl = navel.el.select('span',true).first();
28178         }
28179         
28180         var _this = this;
28181         
28182         if(this.buttons){
28183             Roo.each(_this.buttons, function(e){ // this might need to use render????
28184                Roo.factory(e).render(_this.el);
28185             });
28186         }
28187             
28188         Roo.each(_this.toolbarItems, function(e) {
28189             _this.navgroup.addItem(e);
28190         });
28191         
28192         
28193         this.first = this.navgroup.addItem({
28194             tooltip: this.firstText,
28195             cls: "prev btn-outline-secondary",
28196             html : ' <i class="fa fa-step-backward"></i>',
28197             disabled: true,
28198             preventDefault: true,
28199             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28200         });
28201         
28202         this.prev =  this.navgroup.addItem({
28203             tooltip: this.prevText,
28204             cls: "prev btn-outline-secondary",
28205             html : ' <i class="fa fa-backward"></i>',
28206             disabled: true,
28207             preventDefault: true,
28208             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28209         });
28210     //this.addSeparator();
28211         
28212         
28213         var field = this.navgroup.addItem( {
28214             tagtype : 'span',
28215             cls : 'x-paging-position  btn-outline-secondary',
28216              disabled: true,
28217             html : this.beforePageText  +
28218                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28219                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28220          } ); //?? escaped?
28221         
28222         this.field = field.el.select('input', true).first();
28223         this.field.on("keydown", this.onPagingKeydown, this);
28224         this.field.on("focus", function(){this.dom.select();});
28225     
28226     
28227         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28228         //this.field.setHeight(18);
28229         //this.addSeparator();
28230         this.next = this.navgroup.addItem({
28231             tooltip: this.nextText,
28232             cls: "next btn-outline-secondary",
28233             html : ' <i class="fa fa-forward"></i>',
28234             disabled: true,
28235             preventDefault: true,
28236             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28237         });
28238         this.last = this.navgroup.addItem({
28239             tooltip: this.lastText,
28240             html : ' <i class="fa fa-step-forward"></i>',
28241             cls: "next btn-outline-secondary",
28242             disabled: true,
28243             preventDefault: true,
28244             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28245         });
28246     //this.addSeparator();
28247         this.loading = this.navgroup.addItem({
28248             tooltip: this.refreshText,
28249             cls: "btn-outline-secondary",
28250             html : ' <i class="fa fa-refresh"></i>',
28251             preventDefault: true,
28252             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28253         });
28254         
28255     },
28256
28257     // private
28258     updateInfo : function(){
28259         if(this.displayEl){
28260             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28261             var msg = count == 0 ?
28262                 this.emptyMsg :
28263                 String.format(
28264                     this.displayMsg,
28265                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28266                 );
28267             this.displayEl.update(msg);
28268         }
28269     },
28270
28271     // private
28272     onLoad : function(ds, r, o)
28273     {
28274         this.cursor = o.params && o.params.start ? o.params.start : 0;
28275         
28276         var d = this.getPageData(),
28277             ap = d.activePage,
28278             ps = d.pages;
28279         
28280         
28281         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28282         this.field.dom.value = ap;
28283         this.first.setDisabled(ap == 1);
28284         this.prev.setDisabled(ap == 1);
28285         this.next.setDisabled(ap == ps);
28286         this.last.setDisabled(ap == ps);
28287         this.loading.enable();
28288         this.updateInfo();
28289     },
28290
28291     // private
28292     getPageData : function(){
28293         var total = this.ds.getTotalCount();
28294         return {
28295             total : total,
28296             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28297             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28298         };
28299     },
28300
28301     // private
28302     onLoadError : function(){
28303         this.loading.enable();
28304     },
28305
28306     // private
28307     onPagingKeydown : function(e){
28308         var k = e.getKey();
28309         var d = this.getPageData();
28310         if(k == e.RETURN){
28311             var v = this.field.dom.value, pageNum;
28312             if(!v || isNaN(pageNum = parseInt(v, 10))){
28313                 this.field.dom.value = d.activePage;
28314                 return;
28315             }
28316             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28317             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28318             e.stopEvent();
28319         }
28320         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))
28321         {
28322           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28323           this.field.dom.value = pageNum;
28324           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28325           e.stopEvent();
28326         }
28327         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28328         {
28329           var v = this.field.dom.value, pageNum; 
28330           var increment = (e.shiftKey) ? 10 : 1;
28331           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28332                 increment *= -1;
28333           }
28334           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28335             this.field.dom.value = d.activePage;
28336             return;
28337           }
28338           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28339           {
28340             this.field.dom.value = parseInt(v, 10) + increment;
28341             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28342             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28343           }
28344           e.stopEvent();
28345         }
28346     },
28347
28348     // private
28349     beforeLoad : function(){
28350         if(this.loading){
28351             this.loading.disable();
28352         }
28353     },
28354
28355     // private
28356     onClick : function(which){
28357         
28358         var ds = this.ds;
28359         if (!ds) {
28360             return;
28361         }
28362         
28363         switch(which){
28364             case "first":
28365                 ds.load({params:{start: 0, limit: this.pageSize}});
28366             break;
28367             case "prev":
28368                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28369             break;
28370             case "next":
28371                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28372             break;
28373             case "last":
28374                 var total = ds.getTotalCount();
28375                 var extra = total % this.pageSize;
28376                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28377                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28378             break;
28379             case "refresh":
28380                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28381             break;
28382         }
28383     },
28384
28385     /**
28386      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28387      * @param {Roo.data.Store} store The data store to unbind
28388      */
28389     unbind : function(ds){
28390         ds.un("beforeload", this.beforeLoad, this);
28391         ds.un("load", this.onLoad, this);
28392         ds.un("loadexception", this.onLoadError, this);
28393         ds.un("remove", this.updateInfo, this);
28394         ds.un("add", this.updateInfo, this);
28395         this.ds = undefined;
28396     },
28397
28398     /**
28399      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28400      * @param {Roo.data.Store} store The data store to bind
28401      */
28402     bind : function(ds){
28403         ds.on("beforeload", this.beforeLoad, this);
28404         ds.on("load", this.onLoad, this);
28405         ds.on("loadexception", this.onLoadError, this);
28406         ds.on("remove", this.updateInfo, this);
28407         ds.on("add", this.updateInfo, this);
28408         this.ds = ds;
28409     }
28410 });/*
28411  * - LGPL
28412  *
28413  * element
28414  * 
28415  */
28416
28417 /**
28418  * @class Roo.bootstrap.MessageBar
28419  * @extends Roo.bootstrap.Component
28420  * Bootstrap MessageBar class
28421  * @cfg {String} html contents of the MessageBar
28422  * @cfg {String} weight (info | success | warning | danger) default info
28423  * @cfg {String} beforeClass insert the bar before the given class
28424  * @cfg {Boolean} closable (true | false) default false
28425  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28426  * 
28427  * @constructor
28428  * Create a new Element
28429  * @param {Object} config The config object
28430  */
28431
28432 Roo.bootstrap.MessageBar = function(config){
28433     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28434 };
28435
28436 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28437     
28438     html: '',
28439     weight: 'info',
28440     closable: false,
28441     fixed: false,
28442     beforeClass: 'bootstrap-sticky-wrap',
28443     
28444     getAutoCreate : function(){
28445         
28446         var cfg = {
28447             tag: 'div',
28448             cls: 'alert alert-dismissable alert-' + this.weight,
28449             cn: [
28450                 {
28451                     tag: 'span',
28452                     cls: 'message',
28453                     html: this.html || ''
28454                 }
28455             ]
28456         };
28457         
28458         if(this.fixed){
28459             cfg.cls += ' alert-messages-fixed';
28460         }
28461         
28462         if(this.closable){
28463             cfg.cn.push({
28464                 tag: 'button',
28465                 cls: 'close',
28466                 html: 'x'
28467             });
28468         }
28469         
28470         return cfg;
28471     },
28472     
28473     onRender : function(ct, position)
28474     {
28475         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28476         
28477         if(!this.el){
28478             var cfg = Roo.apply({},  this.getAutoCreate());
28479             cfg.id = Roo.id();
28480             
28481             if (this.cls) {
28482                 cfg.cls += ' ' + this.cls;
28483             }
28484             if (this.style) {
28485                 cfg.style = this.style;
28486             }
28487             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28488             
28489             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28490         }
28491         
28492         this.el.select('>button.close').on('click', this.hide, this);
28493         
28494     },
28495     
28496     show : function()
28497     {
28498         if (!this.rendered) {
28499             this.render();
28500         }
28501         
28502         this.el.show();
28503         
28504         this.fireEvent('show', this);
28505         
28506     },
28507     
28508     hide : function()
28509     {
28510         if (!this.rendered) {
28511             this.render();
28512         }
28513         
28514         this.el.hide();
28515         
28516         this.fireEvent('hide', this);
28517     },
28518     
28519     update : function()
28520     {
28521 //        var e = this.el.dom.firstChild;
28522 //        
28523 //        if(this.closable){
28524 //            e = e.nextSibling;
28525 //        }
28526 //        
28527 //        e.data = this.html || '';
28528
28529         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28530     }
28531    
28532 });
28533
28534  
28535
28536      /*
28537  * - LGPL
28538  *
28539  * Graph
28540  * 
28541  */
28542
28543
28544 /**
28545  * @class Roo.bootstrap.Graph
28546  * @extends Roo.bootstrap.Component
28547  * Bootstrap Graph class
28548 > Prameters
28549  -sm {number} sm 4
28550  -md {number} md 5
28551  @cfg {String} graphtype  bar | vbar | pie
28552  @cfg {number} g_x coodinator | centre x (pie)
28553  @cfg {number} g_y coodinator | centre y (pie)
28554  @cfg {number} g_r radius (pie)
28555  @cfg {number} g_height height of the chart (respected by all elements in the set)
28556  @cfg {number} g_width width of the chart (respected by all elements in the set)
28557  @cfg {Object} title The title of the chart
28558     
28559  -{Array}  values
28560  -opts (object) options for the chart 
28561      o {
28562      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28563      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28564      o vgutter (number)
28565      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.
28566      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28567      o to
28568      o stretch (boolean)
28569      o }
28570  -opts (object) options for the pie
28571      o{
28572      o cut
28573      o startAngle (number)
28574      o endAngle (number)
28575      } 
28576  *
28577  * @constructor
28578  * Create a new Input
28579  * @param {Object} config The config object
28580  */
28581
28582 Roo.bootstrap.Graph = function(config){
28583     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28584     
28585     this.addEvents({
28586         // img events
28587         /**
28588          * @event click
28589          * The img click event for the img.
28590          * @param {Roo.EventObject} e
28591          */
28592         "click" : true
28593     });
28594 };
28595
28596 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28597     
28598     sm: 4,
28599     md: 5,
28600     graphtype: 'bar',
28601     g_height: 250,
28602     g_width: 400,
28603     g_x: 50,
28604     g_y: 50,
28605     g_r: 30,
28606     opts:{
28607         //g_colors: this.colors,
28608         g_type: 'soft',
28609         g_gutter: '20%'
28610
28611     },
28612     title : false,
28613
28614     getAutoCreate : function(){
28615         
28616         var cfg = {
28617             tag: 'div',
28618             html : null
28619         };
28620         
28621         
28622         return  cfg;
28623     },
28624
28625     onRender : function(ct,position){
28626         
28627         
28628         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28629         
28630         if (typeof(Raphael) == 'undefined') {
28631             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28632             return;
28633         }
28634         
28635         this.raphael = Raphael(this.el.dom);
28636         
28637                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28638                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28639                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28640                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28641                 /*
28642                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28643                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28644                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28645                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28646                 
28647                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28648                 r.barchart(330, 10, 300, 220, data1);
28649                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28650                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28651                 */
28652                 
28653                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28654                 // r.barchart(30, 30, 560, 250,  xdata, {
28655                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28656                 //     axis : "0 0 1 1",
28657                 //     axisxlabels :  xdata
28658                 //     //yvalues : cols,
28659                    
28660                 // });
28661 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28662 //        
28663 //        this.load(null,xdata,{
28664 //                axis : "0 0 1 1",
28665 //                axisxlabels :  xdata
28666 //                });
28667
28668     },
28669
28670     load : function(graphtype,xdata,opts)
28671     {
28672         this.raphael.clear();
28673         if(!graphtype) {
28674             graphtype = this.graphtype;
28675         }
28676         if(!opts){
28677             opts = this.opts;
28678         }
28679         var r = this.raphael,
28680             fin = function () {
28681                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28682             },
28683             fout = function () {
28684                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28685             },
28686             pfin = function() {
28687                 this.sector.stop();
28688                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28689
28690                 if (this.label) {
28691                     this.label[0].stop();
28692                     this.label[0].attr({ r: 7.5 });
28693                     this.label[1].attr({ "font-weight": 800 });
28694                 }
28695             },
28696             pfout = function() {
28697                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28698
28699                 if (this.label) {
28700                     this.label[0].animate({ r: 5 }, 500, "bounce");
28701                     this.label[1].attr({ "font-weight": 400 });
28702                 }
28703             };
28704
28705         switch(graphtype){
28706             case 'bar':
28707                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28708                 break;
28709             case 'hbar':
28710                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28711                 break;
28712             case 'pie':
28713 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28714 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28715 //            
28716                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28717                 
28718                 break;
28719
28720         }
28721         
28722         if(this.title){
28723             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28724         }
28725         
28726     },
28727     
28728     setTitle: function(o)
28729     {
28730         this.title = o;
28731     },
28732     
28733     initEvents: function() {
28734         
28735         if(!this.href){
28736             this.el.on('click', this.onClick, this);
28737         }
28738     },
28739     
28740     onClick : function(e)
28741     {
28742         Roo.log('img onclick');
28743         this.fireEvent('click', this, e);
28744     }
28745    
28746 });
28747
28748  
28749 /*
28750  * - LGPL
28751  *
28752  * numberBox
28753  * 
28754  */
28755 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28756
28757 /**
28758  * @class Roo.bootstrap.dash.NumberBox
28759  * @extends Roo.bootstrap.Component
28760  * Bootstrap NumberBox class
28761  * @cfg {String} headline Box headline
28762  * @cfg {String} content Box content
28763  * @cfg {String} icon Box icon
28764  * @cfg {String} footer Footer text
28765  * @cfg {String} fhref Footer href
28766  * 
28767  * @constructor
28768  * Create a new NumberBox
28769  * @param {Object} config The config object
28770  */
28771
28772
28773 Roo.bootstrap.dash.NumberBox = function(config){
28774     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28775     
28776 };
28777
28778 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28779     
28780     headline : '',
28781     content : '',
28782     icon : '',
28783     footer : '',
28784     fhref : '',
28785     ficon : '',
28786     
28787     getAutoCreate : function(){
28788         
28789         var cfg = {
28790             tag : 'div',
28791             cls : 'small-box ',
28792             cn : [
28793                 {
28794                     tag : 'div',
28795                     cls : 'inner',
28796                     cn :[
28797                         {
28798                             tag : 'h3',
28799                             cls : 'roo-headline',
28800                             html : this.headline
28801                         },
28802                         {
28803                             tag : 'p',
28804                             cls : 'roo-content',
28805                             html : this.content
28806                         }
28807                     ]
28808                 }
28809             ]
28810         };
28811         
28812         if(this.icon){
28813             cfg.cn.push({
28814                 tag : 'div',
28815                 cls : 'icon',
28816                 cn :[
28817                     {
28818                         tag : 'i',
28819                         cls : 'ion ' + this.icon
28820                     }
28821                 ]
28822             });
28823         }
28824         
28825         if(this.footer){
28826             var footer = {
28827                 tag : 'a',
28828                 cls : 'small-box-footer',
28829                 href : this.fhref || '#',
28830                 html : this.footer
28831             };
28832             
28833             cfg.cn.push(footer);
28834             
28835         }
28836         
28837         return  cfg;
28838     },
28839
28840     onRender : function(ct,position){
28841         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28842
28843
28844        
28845                 
28846     },
28847
28848     setHeadline: function (value)
28849     {
28850         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28851     },
28852     
28853     setFooter: function (value, href)
28854     {
28855         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28856         
28857         if(href){
28858             this.el.select('a.small-box-footer',true).first().attr('href', href);
28859         }
28860         
28861     },
28862
28863     setContent: function (value)
28864     {
28865         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28866     },
28867
28868     initEvents: function() 
28869     {   
28870         
28871     }
28872     
28873 });
28874
28875  
28876 /*
28877  * - LGPL
28878  *
28879  * TabBox
28880  * 
28881  */
28882 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28883
28884 /**
28885  * @class Roo.bootstrap.dash.TabBox
28886  * @extends Roo.bootstrap.Component
28887  * Bootstrap TabBox class
28888  * @cfg {String} title Title of the TabBox
28889  * @cfg {String} icon Icon of the TabBox
28890  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28891  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28892  * 
28893  * @constructor
28894  * Create a new TabBox
28895  * @param {Object} config The config object
28896  */
28897
28898
28899 Roo.bootstrap.dash.TabBox = function(config){
28900     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28901     this.addEvents({
28902         // raw events
28903         /**
28904          * @event addpane
28905          * When a pane is added
28906          * @param {Roo.bootstrap.dash.TabPane} pane
28907          */
28908         "addpane" : true,
28909         /**
28910          * @event activatepane
28911          * When a pane is activated
28912          * @param {Roo.bootstrap.dash.TabPane} pane
28913          */
28914         "activatepane" : true
28915         
28916          
28917     });
28918     
28919     this.panes = [];
28920 };
28921
28922 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28923
28924     title : '',
28925     icon : false,
28926     showtabs : true,
28927     tabScrollable : false,
28928     
28929     getChildContainer : function()
28930     {
28931         return this.el.select('.tab-content', true).first();
28932     },
28933     
28934     getAutoCreate : function(){
28935         
28936         var header = {
28937             tag: 'li',
28938             cls: 'pull-left header',
28939             html: this.title,
28940             cn : []
28941         };
28942         
28943         if(this.icon){
28944             header.cn.push({
28945                 tag: 'i',
28946                 cls: 'fa ' + this.icon
28947             });
28948         }
28949         
28950         var h = {
28951             tag: 'ul',
28952             cls: 'nav nav-tabs pull-right',
28953             cn: [
28954                 header
28955             ]
28956         };
28957         
28958         if(this.tabScrollable){
28959             h = {
28960                 tag: 'div',
28961                 cls: 'tab-header',
28962                 cn: [
28963                     {
28964                         tag: 'ul',
28965                         cls: 'nav nav-tabs pull-right',
28966                         cn: [
28967                             header
28968                         ]
28969                     }
28970                 ]
28971             };
28972         }
28973         
28974         var cfg = {
28975             tag: 'div',
28976             cls: 'nav-tabs-custom',
28977             cn: [
28978                 h,
28979                 {
28980                     tag: 'div',
28981                     cls: 'tab-content no-padding',
28982                     cn: []
28983                 }
28984             ]
28985         };
28986
28987         return  cfg;
28988     },
28989     initEvents : function()
28990     {
28991         //Roo.log('add add pane handler');
28992         this.on('addpane', this.onAddPane, this);
28993     },
28994      /**
28995      * Updates the box title
28996      * @param {String} html to set the title to.
28997      */
28998     setTitle : function(value)
28999     {
29000         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29001     },
29002     onAddPane : function(pane)
29003     {
29004         this.panes.push(pane);
29005         //Roo.log('addpane');
29006         //Roo.log(pane);
29007         // tabs are rendere left to right..
29008         if(!this.showtabs){
29009             return;
29010         }
29011         
29012         var ctr = this.el.select('.nav-tabs', true).first();
29013          
29014          
29015         var existing = ctr.select('.nav-tab',true);
29016         var qty = existing.getCount();;
29017         
29018         
29019         var tab = ctr.createChild({
29020             tag : 'li',
29021             cls : 'nav-tab' + (qty ? '' : ' active'),
29022             cn : [
29023                 {
29024                     tag : 'a',
29025                     href:'#',
29026                     html : pane.title
29027                 }
29028             ]
29029         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29030         pane.tab = tab;
29031         
29032         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29033         if (!qty) {
29034             pane.el.addClass('active');
29035         }
29036         
29037                 
29038     },
29039     onTabClick : function(ev,un,ob,pane)
29040     {
29041         //Roo.log('tab - prev default');
29042         ev.preventDefault();
29043         
29044         
29045         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29046         pane.tab.addClass('active');
29047         //Roo.log(pane.title);
29048         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29049         // technically we should have a deactivate event.. but maybe add later.
29050         // and it should not de-activate the selected tab...
29051         this.fireEvent('activatepane', pane);
29052         pane.el.addClass('active');
29053         pane.fireEvent('activate');
29054         
29055         
29056     },
29057     
29058     getActivePane : function()
29059     {
29060         var r = false;
29061         Roo.each(this.panes, function(p) {
29062             if(p.el.hasClass('active')){
29063                 r = p;
29064                 return false;
29065             }
29066             
29067             return;
29068         });
29069         
29070         return r;
29071     }
29072     
29073     
29074 });
29075
29076  
29077 /*
29078  * - LGPL
29079  *
29080  * Tab pane
29081  * 
29082  */
29083 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29084 /**
29085  * @class Roo.bootstrap.TabPane
29086  * @extends Roo.bootstrap.Component
29087  * Bootstrap TabPane class
29088  * @cfg {Boolean} active (false | true) Default false
29089  * @cfg {String} title title of panel
29090
29091  * 
29092  * @constructor
29093  * Create a new TabPane
29094  * @param {Object} config The config object
29095  */
29096
29097 Roo.bootstrap.dash.TabPane = function(config){
29098     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29099     
29100     this.addEvents({
29101         // raw events
29102         /**
29103          * @event activate
29104          * When a pane is activated
29105          * @param {Roo.bootstrap.dash.TabPane} pane
29106          */
29107         "activate" : true
29108          
29109     });
29110 };
29111
29112 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29113     
29114     active : false,
29115     title : '',
29116     
29117     // the tabBox that this is attached to.
29118     tab : false,
29119      
29120     getAutoCreate : function() 
29121     {
29122         var cfg = {
29123             tag: 'div',
29124             cls: 'tab-pane'
29125         };
29126         
29127         if(this.active){
29128             cfg.cls += ' active';
29129         }
29130         
29131         return cfg;
29132     },
29133     initEvents  : function()
29134     {
29135         //Roo.log('trigger add pane handler');
29136         this.parent().fireEvent('addpane', this)
29137     },
29138     
29139      /**
29140      * Updates the tab title 
29141      * @param {String} html to set the title to.
29142      */
29143     setTitle: function(str)
29144     {
29145         if (!this.tab) {
29146             return;
29147         }
29148         this.title = str;
29149         this.tab.select('a', true).first().dom.innerHTML = str;
29150         
29151     }
29152     
29153     
29154     
29155 });
29156
29157  
29158
29159
29160  /*
29161  * - LGPL
29162  *
29163  * menu
29164  * 
29165  */
29166 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29167
29168 /**
29169  * @class Roo.bootstrap.menu.Menu
29170  * @extends Roo.bootstrap.Component
29171  * Bootstrap Menu class - container for Menu
29172  * @cfg {String} html Text of the menu
29173  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29174  * @cfg {String} icon Font awesome icon
29175  * @cfg {String} pos Menu align to (top | bottom) default bottom
29176  * 
29177  * 
29178  * @constructor
29179  * Create a new Menu
29180  * @param {Object} config The config object
29181  */
29182
29183
29184 Roo.bootstrap.menu.Menu = function(config){
29185     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29186     
29187     this.addEvents({
29188         /**
29189          * @event beforeshow
29190          * Fires before this menu is displayed
29191          * @param {Roo.bootstrap.menu.Menu} this
29192          */
29193         beforeshow : true,
29194         /**
29195          * @event beforehide
29196          * Fires before this menu is hidden
29197          * @param {Roo.bootstrap.menu.Menu} this
29198          */
29199         beforehide : true,
29200         /**
29201          * @event show
29202          * Fires after this menu is displayed
29203          * @param {Roo.bootstrap.menu.Menu} this
29204          */
29205         show : true,
29206         /**
29207          * @event hide
29208          * Fires after this menu is hidden
29209          * @param {Roo.bootstrap.menu.Menu} this
29210          */
29211         hide : true,
29212         /**
29213          * @event click
29214          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29215          * @param {Roo.bootstrap.menu.Menu} this
29216          * @param {Roo.EventObject} e
29217          */
29218         click : true
29219     });
29220     
29221 };
29222
29223 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29224     
29225     submenu : false,
29226     html : '',
29227     weight : 'default',
29228     icon : false,
29229     pos : 'bottom',
29230     
29231     
29232     getChildContainer : function() {
29233         if(this.isSubMenu){
29234             return this.el;
29235         }
29236         
29237         return this.el.select('ul.dropdown-menu', true).first();  
29238     },
29239     
29240     getAutoCreate : function()
29241     {
29242         var text = [
29243             {
29244                 tag : 'span',
29245                 cls : 'roo-menu-text',
29246                 html : this.html
29247             }
29248         ];
29249         
29250         if(this.icon){
29251             text.unshift({
29252                 tag : 'i',
29253                 cls : 'fa ' + this.icon
29254             })
29255         }
29256         
29257         
29258         var cfg = {
29259             tag : 'div',
29260             cls : 'btn-group',
29261             cn : [
29262                 {
29263                     tag : 'button',
29264                     cls : 'dropdown-button btn btn-' + this.weight,
29265                     cn : text
29266                 },
29267                 {
29268                     tag : 'button',
29269                     cls : 'dropdown-toggle btn btn-' + this.weight,
29270                     cn : [
29271                         {
29272                             tag : 'span',
29273                             cls : 'caret'
29274                         }
29275                     ]
29276                 },
29277                 {
29278                     tag : 'ul',
29279                     cls : 'dropdown-menu'
29280                 }
29281             ]
29282             
29283         };
29284         
29285         if(this.pos == 'top'){
29286             cfg.cls += ' dropup';
29287         }
29288         
29289         if(this.isSubMenu){
29290             cfg = {
29291                 tag : 'ul',
29292                 cls : 'dropdown-menu'
29293             }
29294         }
29295         
29296         return cfg;
29297     },
29298     
29299     onRender : function(ct, position)
29300     {
29301         this.isSubMenu = ct.hasClass('dropdown-submenu');
29302         
29303         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29304     },
29305     
29306     initEvents : function() 
29307     {
29308         if(this.isSubMenu){
29309             return;
29310         }
29311         
29312         this.hidden = true;
29313         
29314         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29315         this.triggerEl.on('click', this.onTriggerPress, this);
29316         
29317         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29318         this.buttonEl.on('click', this.onClick, this);
29319         
29320     },
29321     
29322     list : function()
29323     {
29324         if(this.isSubMenu){
29325             return this.el;
29326         }
29327         
29328         return this.el.select('ul.dropdown-menu', true).first();
29329     },
29330     
29331     onClick : function(e)
29332     {
29333         this.fireEvent("click", this, e);
29334     },
29335     
29336     onTriggerPress  : function(e)
29337     {   
29338         if (this.isVisible()) {
29339             this.hide();
29340         } else {
29341             this.show();
29342         }
29343     },
29344     
29345     isVisible : function(){
29346         return !this.hidden;
29347     },
29348     
29349     show : function()
29350     {
29351         this.fireEvent("beforeshow", this);
29352         
29353         this.hidden = false;
29354         this.el.addClass('open');
29355         
29356         Roo.get(document).on("mouseup", this.onMouseUp, this);
29357         
29358         this.fireEvent("show", this);
29359         
29360         
29361     },
29362     
29363     hide : function()
29364     {
29365         this.fireEvent("beforehide", this);
29366         
29367         this.hidden = true;
29368         this.el.removeClass('open');
29369         
29370         Roo.get(document).un("mouseup", this.onMouseUp);
29371         
29372         this.fireEvent("hide", this);
29373     },
29374     
29375     onMouseUp : function()
29376     {
29377         this.hide();
29378     }
29379     
29380 });
29381
29382  
29383  /*
29384  * - LGPL
29385  *
29386  * menu item
29387  * 
29388  */
29389 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29390
29391 /**
29392  * @class Roo.bootstrap.menu.Item
29393  * @extends Roo.bootstrap.Component
29394  * Bootstrap MenuItem class
29395  * @cfg {Boolean} submenu (true | false) default false
29396  * @cfg {String} html text of the item
29397  * @cfg {String} href the link
29398  * @cfg {Boolean} disable (true | false) default false
29399  * @cfg {Boolean} preventDefault (true | false) default true
29400  * @cfg {String} icon Font awesome icon
29401  * @cfg {String} pos Submenu align to (left | right) default right 
29402  * 
29403  * 
29404  * @constructor
29405  * Create a new Item
29406  * @param {Object} config The config object
29407  */
29408
29409
29410 Roo.bootstrap.menu.Item = function(config){
29411     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29412     this.addEvents({
29413         /**
29414          * @event mouseover
29415          * Fires when the mouse is hovering over this menu
29416          * @param {Roo.bootstrap.menu.Item} this
29417          * @param {Roo.EventObject} e
29418          */
29419         mouseover : true,
29420         /**
29421          * @event mouseout
29422          * Fires when the mouse exits this menu
29423          * @param {Roo.bootstrap.menu.Item} this
29424          * @param {Roo.EventObject} e
29425          */
29426         mouseout : true,
29427         // raw events
29428         /**
29429          * @event click
29430          * The raw click event for the entire grid.
29431          * @param {Roo.EventObject} e
29432          */
29433         click : true
29434     });
29435 };
29436
29437 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29438     
29439     submenu : false,
29440     href : '',
29441     html : '',
29442     preventDefault: true,
29443     disable : false,
29444     icon : false,
29445     pos : 'right',
29446     
29447     getAutoCreate : function()
29448     {
29449         var text = [
29450             {
29451                 tag : 'span',
29452                 cls : 'roo-menu-item-text',
29453                 html : this.html
29454             }
29455         ];
29456         
29457         if(this.icon){
29458             text.unshift({
29459                 tag : 'i',
29460                 cls : 'fa ' + this.icon
29461             })
29462         }
29463         
29464         var cfg = {
29465             tag : 'li',
29466             cn : [
29467                 {
29468                     tag : 'a',
29469                     href : this.href || '#',
29470                     cn : text
29471                 }
29472             ]
29473         };
29474         
29475         if(this.disable){
29476             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29477         }
29478         
29479         if(this.submenu){
29480             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29481             
29482             if(this.pos == 'left'){
29483                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29484             }
29485         }
29486         
29487         return cfg;
29488     },
29489     
29490     initEvents : function() 
29491     {
29492         this.el.on('mouseover', this.onMouseOver, this);
29493         this.el.on('mouseout', this.onMouseOut, this);
29494         
29495         this.el.select('a', true).first().on('click', this.onClick, this);
29496         
29497     },
29498     
29499     onClick : function(e)
29500     {
29501         if(this.preventDefault){
29502             e.preventDefault();
29503         }
29504         
29505         this.fireEvent("click", this, e);
29506     },
29507     
29508     onMouseOver : function(e)
29509     {
29510         if(this.submenu && this.pos == 'left'){
29511             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29512         }
29513         
29514         this.fireEvent("mouseover", this, e);
29515     },
29516     
29517     onMouseOut : function(e)
29518     {
29519         this.fireEvent("mouseout", this, e);
29520     }
29521 });
29522
29523  
29524
29525  /*
29526  * - LGPL
29527  *
29528  * menu separator
29529  * 
29530  */
29531 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29532
29533 /**
29534  * @class Roo.bootstrap.menu.Separator
29535  * @extends Roo.bootstrap.Component
29536  * Bootstrap Separator class
29537  * 
29538  * @constructor
29539  * Create a new Separator
29540  * @param {Object} config The config object
29541  */
29542
29543
29544 Roo.bootstrap.menu.Separator = function(config){
29545     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29546 };
29547
29548 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29549     
29550     getAutoCreate : function(){
29551         var cfg = {
29552             tag : 'li',
29553             cls: 'dropdown-divider divider'
29554         };
29555         
29556         return cfg;
29557     }
29558    
29559 });
29560
29561  
29562
29563  /*
29564  * - LGPL
29565  *
29566  * Tooltip
29567  * 
29568  */
29569
29570 /**
29571  * @class Roo.bootstrap.Tooltip
29572  * Bootstrap Tooltip class
29573  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29574  * to determine which dom element triggers the tooltip.
29575  * 
29576  * It needs to add support for additional attributes like tooltip-position
29577  * 
29578  * @constructor
29579  * Create a new Toolti
29580  * @param {Object} config The config object
29581  */
29582
29583 Roo.bootstrap.Tooltip = function(config){
29584     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29585     
29586     this.alignment = Roo.bootstrap.Tooltip.alignment;
29587     
29588     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29589         this.alignment = config.alignment;
29590     }
29591     
29592 };
29593
29594 Roo.apply(Roo.bootstrap.Tooltip, {
29595     /**
29596      * @function init initialize tooltip monitoring.
29597      * @static
29598      */
29599     currentEl : false,
29600     currentTip : false,
29601     currentRegion : false,
29602     
29603     //  init : delay?
29604     
29605     init : function()
29606     {
29607         Roo.get(document).on('mouseover', this.enter ,this);
29608         Roo.get(document).on('mouseout', this.leave, this);
29609          
29610         
29611         this.currentTip = new Roo.bootstrap.Tooltip();
29612     },
29613     
29614     enter : function(ev)
29615     {
29616         var dom = ev.getTarget();
29617         
29618         //Roo.log(['enter',dom]);
29619         var el = Roo.fly(dom);
29620         if (this.currentEl) {
29621             //Roo.log(dom);
29622             //Roo.log(this.currentEl);
29623             //Roo.log(this.currentEl.contains(dom));
29624             if (this.currentEl == el) {
29625                 return;
29626             }
29627             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29628                 return;
29629             }
29630
29631         }
29632         
29633         if (this.currentTip.el) {
29634             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29635         }    
29636         //Roo.log(ev);
29637         
29638         if(!el || el.dom == document){
29639             return;
29640         }
29641         
29642         var bindEl = el; 
29643         var pel = false;
29644         if (!el.attr('tooltip')) {
29645             pel = el.findParent("[tooltip]");
29646             if (pel) {
29647                 bindEl = Roo.get(pel);
29648             }
29649         }
29650         
29651        
29652         
29653         // you can not look for children, as if el is the body.. then everythign is the child..
29654         if (!pel && !el.attr('tooltip')) { //
29655             if (!el.select("[tooltip]").elements.length) {
29656                 return;
29657             }
29658             // is the mouse over this child...?
29659             bindEl = el.select("[tooltip]").first();
29660             var xy = ev.getXY();
29661             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29662                 //Roo.log("not in region.");
29663                 return;
29664             }
29665             //Roo.log("child element over..");
29666             
29667         }
29668         this.currentEl = el;
29669         this.currentTip.bind(bindEl);
29670         this.currentRegion = Roo.lib.Region.getRegion(dom);
29671         this.currentTip.enter();
29672         
29673     },
29674     leave : function(ev)
29675     {
29676         var dom = ev.getTarget();
29677         //Roo.log(['leave',dom]);
29678         if (!this.currentEl) {
29679             return;
29680         }
29681         
29682         
29683         if (dom != this.currentEl.dom) {
29684             return;
29685         }
29686         var xy = ev.getXY();
29687         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29688             return;
29689         }
29690         // only activate leave if mouse cursor is outside... bounding box..
29691         
29692         
29693         
29694         
29695         if (this.currentTip) {
29696             this.currentTip.leave();
29697         }
29698         //Roo.log('clear currentEl');
29699         this.currentEl = false;
29700         
29701         
29702     },
29703     alignment : {
29704         'left' : ['r-l', [-2,0], 'right'],
29705         'right' : ['l-r', [2,0], 'left'],
29706         'bottom' : ['t-b', [0,2], 'top'],
29707         'top' : [ 'b-t', [0,-2], 'bottom']
29708     }
29709     
29710 });
29711
29712
29713 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29714     
29715     
29716     bindEl : false,
29717     
29718     delay : null, // can be { show : 300 , hide: 500}
29719     
29720     timeout : null,
29721     
29722     hoverState : null, //???
29723     
29724     placement : 'bottom', 
29725     
29726     alignment : false,
29727     
29728     getAutoCreate : function(){
29729     
29730         var cfg = {
29731            cls : 'tooltip',   
29732            role : 'tooltip',
29733            cn : [
29734                 {
29735                     cls : 'tooltip-arrow arrow'
29736                 },
29737                 {
29738                     cls : 'tooltip-inner'
29739                 }
29740            ]
29741         };
29742         
29743         return cfg;
29744     },
29745     bind : function(el)
29746     {
29747         this.bindEl = el;
29748     },
29749     
29750     initEvents : function()
29751     {
29752         this.arrowEl = this.el.select('.arrow', true).first();
29753         this.innerEl = this.el.select('.tooltip-inner', true).first();
29754     },
29755     
29756     enter : function () {
29757        
29758         if (this.timeout != null) {
29759             clearTimeout(this.timeout);
29760         }
29761         
29762         this.hoverState = 'in';
29763          //Roo.log("enter - show");
29764         if (!this.delay || !this.delay.show) {
29765             this.show();
29766             return;
29767         }
29768         var _t = this;
29769         this.timeout = setTimeout(function () {
29770             if (_t.hoverState == 'in') {
29771                 _t.show();
29772             }
29773         }, this.delay.show);
29774     },
29775     leave : function()
29776     {
29777         clearTimeout(this.timeout);
29778     
29779         this.hoverState = 'out';
29780          if (!this.delay || !this.delay.hide) {
29781             this.hide();
29782             return;
29783         }
29784        
29785         var _t = this;
29786         this.timeout = setTimeout(function () {
29787             //Roo.log("leave - timeout");
29788             
29789             if (_t.hoverState == 'out') {
29790                 _t.hide();
29791                 Roo.bootstrap.Tooltip.currentEl = false;
29792             }
29793         }, delay);
29794     },
29795     
29796     show : function (msg)
29797     {
29798         if (!this.el) {
29799             this.render(document.body);
29800         }
29801         // set content.
29802         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29803         
29804         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29805         
29806         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29807         
29808         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29809                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29810         
29811         var placement = typeof this.placement == 'function' ?
29812             this.placement.call(this, this.el, on_el) :
29813             this.placement;
29814             
29815         var autoToken = /\s?auto?\s?/i;
29816         var autoPlace = autoToken.test(placement);
29817         if (autoPlace) {
29818             placement = placement.replace(autoToken, '') || 'top';
29819         }
29820         
29821         //this.el.detach()
29822         //this.el.setXY([0,0]);
29823         this.el.show();
29824         //this.el.dom.style.display='block';
29825         
29826         //this.el.appendTo(on_el);
29827         
29828         var p = this.getPosition();
29829         var box = this.el.getBox();
29830         
29831         if (autoPlace) {
29832             // fixme..
29833         }
29834         
29835         var align = this.alignment[placement];
29836         
29837         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29838         
29839         if(placement == 'top' || placement == 'bottom'){
29840             if(xy[0] < 0){
29841                 placement = 'right';
29842             }
29843             
29844             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29845                 placement = 'left';
29846             }
29847             
29848             var scroll = Roo.select('body', true).first().getScroll();
29849             
29850             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29851                 placement = 'top';
29852             }
29853             
29854             align = this.alignment[placement];
29855             
29856             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29857             
29858         }
29859         
29860         var elems = document.getElementsByTagName('div');
29861         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29862         for (var i = 0; i < elems.length; i++) {
29863           var zindex = Number.parseInt(
29864                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29865                 10
29866           );
29867           if (zindex > highest) {
29868             highest = zindex;
29869           }
29870         }
29871         
29872         
29873         
29874         this.el.dom.style.zIndex = highest;
29875         
29876         this.el.alignTo(this.bindEl, align[0],align[1]);
29877         //var arrow = this.el.select('.arrow',true).first();
29878         //arrow.set(align[2], 
29879         
29880         this.el.addClass(placement);
29881         this.el.addClass("bs-tooltip-"+ placement);
29882         
29883         this.el.addClass('in fade show');
29884         
29885         this.hoverState = null;
29886         
29887         if (this.el.hasClass('fade')) {
29888             // fade it?
29889         }
29890         
29891         
29892         
29893         
29894         
29895     },
29896     hide : function()
29897     {
29898          
29899         if (!this.el) {
29900             return;
29901         }
29902         //this.el.setXY([0,0]);
29903         this.el.removeClass(['show', 'in']);
29904         //this.el.hide();
29905         
29906     }
29907     
29908 });
29909  
29910
29911  /*
29912  * - LGPL
29913  *
29914  * Location Picker
29915  * 
29916  */
29917
29918 /**
29919  * @class Roo.bootstrap.LocationPicker
29920  * @extends Roo.bootstrap.Component
29921  * Bootstrap LocationPicker class
29922  * @cfg {Number} latitude Position when init default 0
29923  * @cfg {Number} longitude Position when init default 0
29924  * @cfg {Number} zoom default 15
29925  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29926  * @cfg {Boolean} mapTypeControl default false
29927  * @cfg {Boolean} disableDoubleClickZoom default false
29928  * @cfg {Boolean} scrollwheel default true
29929  * @cfg {Boolean} streetViewControl default false
29930  * @cfg {Number} radius default 0
29931  * @cfg {String} locationName
29932  * @cfg {Boolean} draggable default true
29933  * @cfg {Boolean} enableAutocomplete default false
29934  * @cfg {Boolean} enableReverseGeocode default true
29935  * @cfg {String} markerTitle
29936  * 
29937  * @constructor
29938  * Create a new LocationPicker
29939  * @param {Object} config The config object
29940  */
29941
29942
29943 Roo.bootstrap.LocationPicker = function(config){
29944     
29945     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29946     
29947     this.addEvents({
29948         /**
29949          * @event initial
29950          * Fires when the picker initialized.
29951          * @param {Roo.bootstrap.LocationPicker} this
29952          * @param {Google Location} location
29953          */
29954         initial : true,
29955         /**
29956          * @event positionchanged
29957          * Fires when the picker position changed.
29958          * @param {Roo.bootstrap.LocationPicker} this
29959          * @param {Google Location} location
29960          */
29961         positionchanged : true,
29962         /**
29963          * @event resize
29964          * Fires when the map resize.
29965          * @param {Roo.bootstrap.LocationPicker} this
29966          */
29967         resize : true,
29968         /**
29969          * @event show
29970          * Fires when the map show.
29971          * @param {Roo.bootstrap.LocationPicker} this
29972          */
29973         show : true,
29974         /**
29975          * @event hide
29976          * Fires when the map hide.
29977          * @param {Roo.bootstrap.LocationPicker} this
29978          */
29979         hide : true,
29980         /**
29981          * @event mapClick
29982          * Fires when click the map.
29983          * @param {Roo.bootstrap.LocationPicker} this
29984          * @param {Map event} e
29985          */
29986         mapClick : true,
29987         /**
29988          * @event mapRightClick
29989          * Fires when right click the map.
29990          * @param {Roo.bootstrap.LocationPicker} this
29991          * @param {Map event} e
29992          */
29993         mapRightClick : true,
29994         /**
29995          * @event markerClick
29996          * Fires when click the marker.
29997          * @param {Roo.bootstrap.LocationPicker} this
29998          * @param {Map event} e
29999          */
30000         markerClick : true,
30001         /**
30002          * @event markerRightClick
30003          * Fires when right click the marker.
30004          * @param {Roo.bootstrap.LocationPicker} this
30005          * @param {Map event} e
30006          */
30007         markerRightClick : true,
30008         /**
30009          * @event OverlayViewDraw
30010          * Fires when OverlayView Draw
30011          * @param {Roo.bootstrap.LocationPicker} this
30012          */
30013         OverlayViewDraw : true,
30014         /**
30015          * @event OverlayViewOnAdd
30016          * Fires when OverlayView Draw
30017          * @param {Roo.bootstrap.LocationPicker} this
30018          */
30019         OverlayViewOnAdd : true,
30020         /**
30021          * @event OverlayViewOnRemove
30022          * Fires when OverlayView Draw
30023          * @param {Roo.bootstrap.LocationPicker} this
30024          */
30025         OverlayViewOnRemove : true,
30026         /**
30027          * @event OverlayViewShow
30028          * Fires when OverlayView Draw
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          * @param {Pixel} cpx
30031          */
30032         OverlayViewShow : true,
30033         /**
30034          * @event OverlayViewHide
30035          * Fires when OverlayView Draw
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          */
30038         OverlayViewHide : true,
30039         /**
30040          * @event loadexception
30041          * Fires when load google lib failed.
30042          * @param {Roo.bootstrap.LocationPicker} this
30043          */
30044         loadexception : true
30045     });
30046         
30047 };
30048
30049 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30050     
30051     gMapContext: false,
30052     
30053     latitude: 0,
30054     longitude: 0,
30055     zoom: 15,
30056     mapTypeId: false,
30057     mapTypeControl: false,
30058     disableDoubleClickZoom: false,
30059     scrollwheel: true,
30060     streetViewControl: false,
30061     radius: 0,
30062     locationName: '',
30063     draggable: true,
30064     enableAutocomplete: false,
30065     enableReverseGeocode: true,
30066     markerTitle: '',
30067     
30068     getAutoCreate: function()
30069     {
30070
30071         var cfg = {
30072             tag: 'div',
30073             cls: 'roo-location-picker'
30074         };
30075         
30076         return cfg
30077     },
30078     
30079     initEvents: function(ct, position)
30080     {       
30081         if(!this.el.getWidth() || this.isApplied()){
30082             return;
30083         }
30084         
30085         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30086         
30087         this.initial();
30088     },
30089     
30090     initial: function()
30091     {
30092         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30093             this.fireEvent('loadexception', this);
30094             return;
30095         }
30096         
30097         if(!this.mapTypeId){
30098             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30099         }
30100         
30101         this.gMapContext = this.GMapContext();
30102         
30103         this.initOverlayView();
30104         
30105         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30106         
30107         var _this = this;
30108                 
30109         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30110             _this.setPosition(_this.gMapContext.marker.position);
30111         });
30112         
30113         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30114             _this.fireEvent('mapClick', this, event);
30115             
30116         });
30117
30118         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30119             _this.fireEvent('mapRightClick', this, event);
30120             
30121         });
30122         
30123         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30124             _this.fireEvent('markerClick', this, event);
30125             
30126         });
30127
30128         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30129             _this.fireEvent('markerRightClick', this, event);
30130             
30131         });
30132         
30133         this.setPosition(this.gMapContext.location);
30134         
30135         this.fireEvent('initial', this, this.gMapContext.location);
30136     },
30137     
30138     initOverlayView: function()
30139     {
30140         var _this = this;
30141         
30142         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30143             
30144             draw: function()
30145             {
30146                 _this.fireEvent('OverlayViewDraw', _this);
30147             },
30148             
30149             onAdd: function()
30150             {
30151                 _this.fireEvent('OverlayViewOnAdd', _this);
30152             },
30153             
30154             onRemove: function()
30155             {
30156                 _this.fireEvent('OverlayViewOnRemove', _this);
30157             },
30158             
30159             show: function(cpx)
30160             {
30161                 _this.fireEvent('OverlayViewShow', _this, cpx);
30162             },
30163             
30164             hide: function()
30165             {
30166                 _this.fireEvent('OverlayViewHide', _this);
30167             }
30168             
30169         });
30170     },
30171     
30172     fromLatLngToContainerPixel: function(event)
30173     {
30174         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30175     },
30176     
30177     isApplied: function() 
30178     {
30179         return this.getGmapContext() == false ? false : true;
30180     },
30181     
30182     getGmapContext: function() 
30183     {
30184         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30185     },
30186     
30187     GMapContext: function() 
30188     {
30189         var position = new google.maps.LatLng(this.latitude, this.longitude);
30190         
30191         var _map = new google.maps.Map(this.el.dom, {
30192             center: position,
30193             zoom: this.zoom,
30194             mapTypeId: this.mapTypeId,
30195             mapTypeControl: this.mapTypeControl,
30196             disableDoubleClickZoom: this.disableDoubleClickZoom,
30197             scrollwheel: this.scrollwheel,
30198             streetViewControl: this.streetViewControl,
30199             locationName: this.locationName,
30200             draggable: this.draggable,
30201             enableAutocomplete: this.enableAutocomplete,
30202             enableReverseGeocode: this.enableReverseGeocode
30203         });
30204         
30205         var _marker = new google.maps.Marker({
30206             position: position,
30207             map: _map,
30208             title: this.markerTitle,
30209             draggable: this.draggable
30210         });
30211         
30212         return {
30213             map: _map,
30214             marker: _marker,
30215             circle: null,
30216             location: position,
30217             radius: this.radius,
30218             locationName: this.locationName,
30219             addressComponents: {
30220                 formatted_address: null,
30221                 addressLine1: null,
30222                 addressLine2: null,
30223                 streetName: null,
30224                 streetNumber: null,
30225                 city: null,
30226                 district: null,
30227                 state: null,
30228                 stateOrProvince: null
30229             },
30230             settings: this,
30231             domContainer: this.el.dom,
30232             geodecoder: new google.maps.Geocoder()
30233         };
30234     },
30235     
30236     drawCircle: function(center, radius, options) 
30237     {
30238         if (this.gMapContext.circle != null) {
30239             this.gMapContext.circle.setMap(null);
30240         }
30241         if (radius > 0) {
30242             radius *= 1;
30243             options = Roo.apply({}, options, {
30244                 strokeColor: "#0000FF",
30245                 strokeOpacity: .35,
30246                 strokeWeight: 2,
30247                 fillColor: "#0000FF",
30248                 fillOpacity: .2
30249             });
30250             
30251             options.map = this.gMapContext.map;
30252             options.radius = radius;
30253             options.center = center;
30254             this.gMapContext.circle = new google.maps.Circle(options);
30255             return this.gMapContext.circle;
30256         }
30257         
30258         return null;
30259     },
30260     
30261     setPosition: function(location) 
30262     {
30263         this.gMapContext.location = location;
30264         this.gMapContext.marker.setPosition(location);
30265         this.gMapContext.map.panTo(location);
30266         this.drawCircle(location, this.gMapContext.radius, {});
30267         
30268         var _this = this;
30269         
30270         if (this.gMapContext.settings.enableReverseGeocode) {
30271             this.gMapContext.geodecoder.geocode({
30272                 latLng: this.gMapContext.location
30273             }, function(results, status) {
30274                 
30275                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30276                     _this.gMapContext.locationName = results[0].formatted_address;
30277                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30278                     
30279                     _this.fireEvent('positionchanged', this, location);
30280                 }
30281             });
30282             
30283             return;
30284         }
30285         
30286         this.fireEvent('positionchanged', this, location);
30287     },
30288     
30289     resize: function()
30290     {
30291         google.maps.event.trigger(this.gMapContext.map, "resize");
30292         
30293         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30294         
30295         this.fireEvent('resize', this);
30296     },
30297     
30298     setPositionByLatLng: function(latitude, longitude)
30299     {
30300         this.setPosition(new google.maps.LatLng(latitude, longitude));
30301     },
30302     
30303     getCurrentPosition: function() 
30304     {
30305         return {
30306             latitude: this.gMapContext.location.lat(),
30307             longitude: this.gMapContext.location.lng()
30308         };
30309     },
30310     
30311     getAddressName: function() 
30312     {
30313         return this.gMapContext.locationName;
30314     },
30315     
30316     getAddressComponents: function() 
30317     {
30318         return this.gMapContext.addressComponents;
30319     },
30320     
30321     address_component_from_google_geocode: function(address_components) 
30322     {
30323         var result = {};
30324         
30325         for (var i = 0; i < address_components.length; i++) {
30326             var component = address_components[i];
30327             if (component.types.indexOf("postal_code") >= 0) {
30328                 result.postalCode = component.short_name;
30329             } else if (component.types.indexOf("street_number") >= 0) {
30330                 result.streetNumber = component.short_name;
30331             } else if (component.types.indexOf("route") >= 0) {
30332                 result.streetName = component.short_name;
30333             } else if (component.types.indexOf("neighborhood") >= 0) {
30334                 result.city = component.short_name;
30335             } else if (component.types.indexOf("locality") >= 0) {
30336                 result.city = component.short_name;
30337             } else if (component.types.indexOf("sublocality") >= 0) {
30338                 result.district = component.short_name;
30339             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30340                 result.stateOrProvince = component.short_name;
30341             } else if (component.types.indexOf("country") >= 0) {
30342                 result.country = component.short_name;
30343             }
30344         }
30345         
30346         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30347         result.addressLine2 = "";
30348         return result;
30349     },
30350     
30351     setZoomLevel: function(zoom)
30352     {
30353         this.gMapContext.map.setZoom(zoom);
30354     },
30355     
30356     show: function()
30357     {
30358         if(!this.el){
30359             return;
30360         }
30361         
30362         this.el.show();
30363         
30364         this.resize();
30365         
30366         this.fireEvent('show', this);
30367     },
30368     
30369     hide: function()
30370     {
30371         if(!this.el){
30372             return;
30373         }
30374         
30375         this.el.hide();
30376         
30377         this.fireEvent('hide', this);
30378     }
30379     
30380 });
30381
30382 Roo.apply(Roo.bootstrap.LocationPicker, {
30383     
30384     OverlayView : function(map, options)
30385     {
30386         options = options || {};
30387         
30388         this.setMap(map);
30389     }
30390     
30391     
30392 });/**
30393  * @class Roo.bootstrap.Alert
30394  * @extends Roo.bootstrap.Component
30395  * Bootstrap Alert class - shows an alert area box
30396  * eg
30397  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30398   Enter a valid email address
30399 </div>
30400  * @licence LGPL
30401  * @cfg {String} title The title of alert
30402  * @cfg {String} html The content of alert
30403  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30404  * @cfg {String} fa font-awesomeicon
30405  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30406  * @cfg {Boolean} close true to show a x closer
30407  * 
30408  * 
30409  * @constructor
30410  * Create a new alert
30411  * @param {Object} config The config object
30412  */
30413
30414
30415 Roo.bootstrap.Alert = function(config){
30416     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30417     
30418 };
30419
30420 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30421     
30422     title: '',
30423     html: '',
30424     weight: false,
30425     fa: false,
30426     faicon: false, // BC
30427     close : false,
30428     
30429     
30430     getAutoCreate : function()
30431     {
30432         
30433         var cfg = {
30434             tag : 'div',
30435             cls : 'alert',
30436             cn : [
30437                 {
30438                     tag: 'button',
30439                     type :  "button",
30440                     cls: "close",
30441                     html : '×',
30442                     style : this.close ? '' : 'display:none'
30443                 },
30444                 {
30445                     tag : 'i',
30446                     cls : 'roo-alert-icon'
30447                     
30448                 },
30449                 {
30450                     tag : 'b',
30451                     cls : 'roo-alert-title',
30452                     html : this.title
30453                 },
30454                 {
30455                     tag : 'span',
30456                     cls : 'roo-alert-text',
30457                     html : this.html
30458                 }
30459             ]
30460         };
30461         
30462         if(this.faicon){
30463             cfg.cn[0].cls += ' fa ' + this.faicon;
30464         }
30465         if(this.fa){
30466             cfg.cn[0].cls += ' fa ' + this.fa;
30467         }
30468         
30469         if(this.weight){
30470             cfg.cls += ' alert-' + this.weight;
30471         }
30472         
30473         return cfg;
30474     },
30475     
30476     initEvents: function() 
30477     {
30478         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30479         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30480         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30481         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30482         if (this.seconds > 0) {
30483             this.hide.defer(this.seconds, this);
30484         }
30485     },
30486     /**
30487      * Set the Title Message HTML
30488      * @param {String} html
30489      */
30490     setTitle : function(str)
30491     {
30492         this.titleEl.dom.innerHTML = str;
30493     },
30494      
30495      /**
30496      * Set the Body Message HTML
30497      * @param {String} html
30498      */
30499     setHtml : function(str)
30500     {
30501         this.htmlEl.dom.innerHTML = str;
30502     },
30503     /**
30504      * Set the Weight of the alert
30505      * @param {String} (success|info|warning|danger) weight
30506      */
30507     
30508     setWeight : function(weight)
30509     {
30510         if(this.weight){
30511             this.el.removeClass('alert-' + this.weight);
30512         }
30513         
30514         this.weight = weight;
30515         
30516         this.el.addClass('alert-' + this.weight);
30517     },
30518       /**
30519      * Set the Icon of the alert
30520      * @param {String} see fontawsome names (name without the 'fa-' bit)
30521      */
30522     setIcon : function(icon)
30523     {
30524         if(this.faicon){
30525             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30526         }
30527         
30528         this.faicon = icon;
30529         
30530         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30531     },
30532     /**
30533      * Hide the Alert
30534      */
30535     hide: function() 
30536     {
30537         this.el.hide();   
30538     },
30539     /**
30540      * Show the Alert
30541      */
30542     show: function() 
30543     {  
30544         this.el.show();   
30545     }
30546     
30547 });
30548
30549  
30550 /*
30551 * Licence: LGPL
30552 */
30553
30554 /**
30555  * @class Roo.bootstrap.UploadCropbox
30556  * @extends Roo.bootstrap.Component
30557  * Bootstrap UploadCropbox class
30558  * @cfg {String} emptyText show when image has been loaded
30559  * @cfg {String} rotateNotify show when image too small to rotate
30560  * @cfg {Number} errorTimeout default 3000
30561  * @cfg {Number} minWidth default 300
30562  * @cfg {Number} minHeight default 300
30563  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30564  * @cfg {Boolean} isDocument (true|false) default false
30565  * @cfg {String} url action url
30566  * @cfg {String} paramName default 'imageUpload'
30567  * @cfg {String} method default POST
30568  * @cfg {Boolean} loadMask (true|false) default true
30569  * @cfg {Boolean} loadingText default 'Loading...'
30570  * 
30571  * @constructor
30572  * Create a new UploadCropbox
30573  * @param {Object} config The config object
30574  */
30575
30576 Roo.bootstrap.UploadCropbox = function(config){
30577     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30578     
30579     this.addEvents({
30580         /**
30581          * @event beforeselectfile
30582          * Fire before select file
30583          * @param {Roo.bootstrap.UploadCropbox} this
30584          */
30585         "beforeselectfile" : true,
30586         /**
30587          * @event initial
30588          * Fire after initEvent
30589          * @param {Roo.bootstrap.UploadCropbox} this
30590          */
30591         "initial" : true,
30592         /**
30593          * @event crop
30594          * Fire after initEvent
30595          * @param {Roo.bootstrap.UploadCropbox} this
30596          * @param {String} data
30597          */
30598         "crop" : true,
30599         /**
30600          * @event prepare
30601          * Fire when preparing the file data
30602          * @param {Roo.bootstrap.UploadCropbox} this
30603          * @param {Object} file
30604          */
30605         "prepare" : true,
30606         /**
30607          * @event exception
30608          * Fire when get exception
30609          * @param {Roo.bootstrap.UploadCropbox} this
30610          * @param {XMLHttpRequest} xhr
30611          */
30612         "exception" : true,
30613         /**
30614          * @event beforeloadcanvas
30615          * Fire before load the canvas
30616          * @param {Roo.bootstrap.UploadCropbox} this
30617          * @param {String} src
30618          */
30619         "beforeloadcanvas" : true,
30620         /**
30621          * @event trash
30622          * Fire when trash image
30623          * @param {Roo.bootstrap.UploadCropbox} this
30624          */
30625         "trash" : true,
30626         /**
30627          * @event download
30628          * Fire when download the image
30629          * @param {Roo.bootstrap.UploadCropbox} this
30630          */
30631         "download" : true,
30632         /**
30633          * @event footerbuttonclick
30634          * Fire when footerbuttonclick
30635          * @param {Roo.bootstrap.UploadCropbox} this
30636          * @param {String} type
30637          */
30638         "footerbuttonclick" : true,
30639         /**
30640          * @event resize
30641          * Fire when resize
30642          * @param {Roo.bootstrap.UploadCropbox} this
30643          */
30644         "resize" : true,
30645         /**
30646          * @event rotate
30647          * Fire when rotate the image
30648          * @param {Roo.bootstrap.UploadCropbox} this
30649          * @param {String} pos
30650          */
30651         "rotate" : true,
30652         /**
30653          * @event inspect
30654          * Fire when inspect the file
30655          * @param {Roo.bootstrap.UploadCropbox} this
30656          * @param {Object} file
30657          */
30658         "inspect" : true,
30659         /**
30660          * @event upload
30661          * Fire when xhr upload the file
30662          * @param {Roo.bootstrap.UploadCropbox} this
30663          * @param {Object} data
30664          */
30665         "upload" : true,
30666         /**
30667          * @event arrange
30668          * Fire when arrange the file data
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          * @param {Object} formData
30671          */
30672         "arrange" : true
30673     });
30674     
30675     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30676 };
30677
30678 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30679     
30680     emptyText : 'Click to upload image',
30681     rotateNotify : 'Image is too small to rotate',
30682     errorTimeout : 3000,
30683     scale : 0,
30684     baseScale : 1,
30685     rotate : 0,
30686     dragable : false,
30687     pinching : false,
30688     mouseX : 0,
30689     mouseY : 0,
30690     cropData : false,
30691     minWidth : 300,
30692     minHeight : 300,
30693     file : false,
30694     exif : {},
30695     baseRotate : 1,
30696     cropType : 'image/jpeg',
30697     buttons : false,
30698     canvasLoaded : false,
30699     isDocument : false,
30700     method : 'POST',
30701     paramName : 'imageUpload',
30702     loadMask : true,
30703     loadingText : 'Loading...',
30704     maskEl : false,
30705     
30706     getAutoCreate : function()
30707     {
30708         var cfg = {
30709             tag : 'div',
30710             cls : 'roo-upload-cropbox',
30711             cn : [
30712                 {
30713                     tag : 'input',
30714                     cls : 'roo-upload-cropbox-selector',
30715                     type : 'file'
30716                 },
30717                 {
30718                     tag : 'div',
30719                     cls : 'roo-upload-cropbox-body',
30720                     style : 'cursor:pointer',
30721                     cn : [
30722                         {
30723                             tag : 'div',
30724                             cls : 'roo-upload-cropbox-preview'
30725                         },
30726                         {
30727                             tag : 'div',
30728                             cls : 'roo-upload-cropbox-thumb'
30729                         },
30730                         {
30731                             tag : 'div',
30732                             cls : 'roo-upload-cropbox-empty-notify',
30733                             html : this.emptyText
30734                         },
30735                         {
30736                             tag : 'div',
30737                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30738                             html : this.rotateNotify
30739                         }
30740                     ]
30741                 },
30742                 {
30743                     tag : 'div',
30744                     cls : 'roo-upload-cropbox-footer',
30745                     cn : {
30746                         tag : 'div',
30747                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30748                         cn : []
30749                     }
30750                 }
30751             ]
30752         };
30753         
30754         return cfg;
30755     },
30756     
30757     onRender : function(ct, position)
30758     {
30759         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30760         
30761         if (this.buttons.length) {
30762             
30763             Roo.each(this.buttons, function(bb) {
30764                 
30765                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30766                 
30767                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30768                 
30769             }, this);
30770         }
30771         
30772         if(this.loadMask){
30773             this.maskEl = this.el;
30774         }
30775     },
30776     
30777     initEvents : function()
30778     {
30779         this.urlAPI = (window.createObjectURL && window) || 
30780                                 (window.URL && URL.revokeObjectURL && URL) || 
30781                                 (window.webkitURL && webkitURL);
30782                         
30783         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30784         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30785         
30786         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30787         this.selectorEl.hide();
30788         
30789         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30790         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30791         
30792         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30793         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794         this.thumbEl.hide();
30795         
30796         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30797         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30798         
30799         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30800         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801         this.errorEl.hide();
30802         
30803         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30804         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30805         this.footerEl.hide();
30806         
30807         this.setThumbBoxSize();
30808         
30809         this.bind();
30810         
30811         this.resize();
30812         
30813         this.fireEvent('initial', this);
30814     },
30815
30816     bind : function()
30817     {
30818         var _this = this;
30819         
30820         window.addEventListener("resize", function() { _this.resize(); } );
30821         
30822         this.bodyEl.on('click', this.beforeSelectFile, this);
30823         
30824         if(Roo.isTouch){
30825             this.bodyEl.on('touchstart', this.onTouchStart, this);
30826             this.bodyEl.on('touchmove', this.onTouchMove, this);
30827             this.bodyEl.on('touchend', this.onTouchEnd, this);
30828         }
30829         
30830         if(!Roo.isTouch){
30831             this.bodyEl.on('mousedown', this.onMouseDown, this);
30832             this.bodyEl.on('mousemove', this.onMouseMove, this);
30833             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30834             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30835             Roo.get(document).on('mouseup', this.onMouseUp, this);
30836         }
30837         
30838         this.selectorEl.on('change', this.onFileSelected, this);
30839     },
30840     
30841     reset : function()
30842     {    
30843         this.scale = 0;
30844         this.baseScale = 1;
30845         this.rotate = 0;
30846         this.baseRotate = 1;
30847         this.dragable = false;
30848         this.pinching = false;
30849         this.mouseX = 0;
30850         this.mouseY = 0;
30851         this.cropData = false;
30852         this.notifyEl.dom.innerHTML = this.emptyText;
30853         
30854         this.selectorEl.dom.value = '';
30855         
30856     },
30857     
30858     resize : function()
30859     {
30860         if(this.fireEvent('resize', this) != false){
30861             this.setThumbBoxPosition();
30862             this.setCanvasPosition();
30863         }
30864     },
30865     
30866     onFooterButtonClick : function(e, el, o, type)
30867     {
30868         switch (type) {
30869             case 'rotate-left' :
30870                 this.onRotateLeft(e);
30871                 break;
30872             case 'rotate-right' :
30873                 this.onRotateRight(e);
30874                 break;
30875             case 'picture' :
30876                 this.beforeSelectFile(e);
30877                 break;
30878             case 'trash' :
30879                 this.trash(e);
30880                 break;
30881             case 'crop' :
30882                 this.crop(e);
30883                 break;
30884             case 'download' :
30885                 this.download(e);
30886                 break;
30887             default :
30888                 break;
30889         }
30890         
30891         this.fireEvent('footerbuttonclick', this, type);
30892     },
30893     
30894     beforeSelectFile : function(e)
30895     {
30896         e.preventDefault();
30897         
30898         if(this.fireEvent('beforeselectfile', this) != false){
30899             this.selectorEl.dom.click();
30900         }
30901     },
30902     
30903     onFileSelected : function(e)
30904     {
30905         e.preventDefault();
30906         
30907         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30908             return;
30909         }
30910         
30911         var file = this.selectorEl.dom.files[0];
30912         
30913         if(this.fireEvent('inspect', this, file) != false){
30914             this.prepare(file);
30915         }
30916         
30917     },
30918     
30919     trash : function(e)
30920     {
30921         this.fireEvent('trash', this);
30922     },
30923     
30924     download : function(e)
30925     {
30926         this.fireEvent('download', this);
30927     },
30928     
30929     loadCanvas : function(src)
30930     {   
30931         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30932             
30933             this.reset();
30934             
30935             this.imageEl = document.createElement('img');
30936             
30937             var _this = this;
30938             
30939             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30940             
30941             this.imageEl.src = src;
30942         }
30943     },
30944     
30945     onLoadCanvas : function()
30946     {   
30947         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30948         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30949         
30950         this.bodyEl.un('click', this.beforeSelectFile, this);
30951         
30952         this.notifyEl.hide();
30953         this.thumbEl.show();
30954         this.footerEl.show();
30955         
30956         this.baseRotateLevel();
30957         
30958         if(this.isDocument){
30959             this.setThumbBoxSize();
30960         }
30961         
30962         this.setThumbBoxPosition();
30963         
30964         this.baseScaleLevel();
30965         
30966         this.draw();
30967         
30968         this.resize();
30969         
30970         this.canvasLoaded = true;
30971         
30972         if(this.loadMask){
30973             this.maskEl.unmask();
30974         }
30975         
30976     },
30977     
30978     setCanvasPosition : function()
30979     {   
30980         if(!this.canvasEl){
30981             return;
30982         }
30983         
30984         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30985         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30986         
30987         this.previewEl.setLeft(pw);
30988         this.previewEl.setTop(ph);
30989         
30990     },
30991     
30992     onMouseDown : function(e)
30993     {   
30994         e.stopEvent();
30995         
30996         this.dragable = true;
30997         this.pinching = false;
30998         
30999         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31000             this.dragable = false;
31001             return;
31002         }
31003         
31004         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31005         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31006         
31007     },
31008     
31009     onMouseMove : function(e)
31010     {   
31011         e.stopEvent();
31012         
31013         if(!this.canvasLoaded){
31014             return;
31015         }
31016         
31017         if (!this.dragable){
31018             return;
31019         }
31020         
31021         var minX = Math.ceil(this.thumbEl.getLeft(true));
31022         var minY = Math.ceil(this.thumbEl.getTop(true));
31023         
31024         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31025         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31026         
31027         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31028         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31029         
31030         x = x - this.mouseX;
31031         y = y - this.mouseY;
31032         
31033         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31034         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31035         
31036         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31037         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31038         
31039         this.previewEl.setLeft(bgX);
31040         this.previewEl.setTop(bgY);
31041         
31042         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31043         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31044     },
31045     
31046     onMouseUp : function(e)
31047     {   
31048         e.stopEvent();
31049         
31050         this.dragable = false;
31051     },
31052     
31053     onMouseWheel : function(e)
31054     {   
31055         e.stopEvent();
31056         
31057         this.startScale = this.scale;
31058         
31059         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31060         
31061         if(!this.zoomable()){
31062             this.scale = this.startScale;
31063             return;
31064         }
31065         
31066         this.draw();
31067         
31068         return;
31069     },
31070     
31071     zoomable : function()
31072     {
31073         var minScale = this.thumbEl.getWidth() / this.minWidth;
31074         
31075         if(this.minWidth < this.minHeight){
31076             minScale = this.thumbEl.getHeight() / this.minHeight;
31077         }
31078         
31079         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31080         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31081         
31082         if(
31083                 this.isDocument &&
31084                 (this.rotate == 0 || this.rotate == 180) && 
31085                 (
31086                     width > this.imageEl.OriginWidth || 
31087                     height > this.imageEl.OriginHeight ||
31088                     (width < this.minWidth && height < this.minHeight)
31089                 )
31090         ){
31091             return false;
31092         }
31093         
31094         if(
31095                 this.isDocument &&
31096                 (this.rotate == 90 || this.rotate == 270) && 
31097                 (
31098                     width > this.imageEl.OriginWidth || 
31099                     height > this.imageEl.OriginHeight ||
31100                     (width < this.minHeight && height < this.minWidth)
31101                 )
31102         ){
31103             return false;
31104         }
31105         
31106         if(
31107                 !this.isDocument &&
31108                 (this.rotate == 0 || this.rotate == 180) && 
31109                 (
31110                     width < this.minWidth || 
31111                     width > this.imageEl.OriginWidth || 
31112                     height < this.minHeight || 
31113                     height > this.imageEl.OriginHeight
31114                 )
31115         ){
31116             return false;
31117         }
31118         
31119         if(
31120                 !this.isDocument &&
31121                 (this.rotate == 90 || this.rotate == 270) && 
31122                 (
31123                     width < this.minHeight || 
31124                     width > this.imageEl.OriginWidth || 
31125                     height < this.minWidth || 
31126                     height > this.imageEl.OriginHeight
31127                 )
31128         ){
31129             return false;
31130         }
31131         
31132         return true;
31133         
31134     },
31135     
31136     onRotateLeft : function(e)
31137     {   
31138         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31139             
31140             var minScale = this.thumbEl.getWidth() / this.minWidth;
31141             
31142             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31143             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31144             
31145             this.startScale = this.scale;
31146             
31147             while (this.getScaleLevel() < minScale){
31148             
31149                 this.scale = this.scale + 1;
31150                 
31151                 if(!this.zoomable()){
31152                     break;
31153                 }
31154                 
31155                 if(
31156                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31157                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31158                 ){
31159                     continue;
31160                 }
31161                 
31162                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31163
31164                 this.draw();
31165                 
31166                 return;
31167             }
31168             
31169             this.scale = this.startScale;
31170             
31171             this.onRotateFail();
31172             
31173             return false;
31174         }
31175         
31176         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31177
31178         if(this.isDocument){
31179             this.setThumbBoxSize();
31180             this.setThumbBoxPosition();
31181             this.setCanvasPosition();
31182         }
31183         
31184         this.draw();
31185         
31186         this.fireEvent('rotate', this, 'left');
31187         
31188     },
31189     
31190     onRotateRight : function(e)
31191     {
31192         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31193             
31194             var minScale = this.thumbEl.getWidth() / this.minWidth;
31195         
31196             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31197             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31198             
31199             this.startScale = this.scale;
31200             
31201             while (this.getScaleLevel() < minScale){
31202             
31203                 this.scale = this.scale + 1;
31204                 
31205                 if(!this.zoomable()){
31206                     break;
31207                 }
31208                 
31209                 if(
31210                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31211                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31212                 ){
31213                     continue;
31214                 }
31215                 
31216                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31217
31218                 this.draw();
31219                 
31220                 return;
31221             }
31222             
31223             this.scale = this.startScale;
31224             
31225             this.onRotateFail();
31226             
31227             return false;
31228         }
31229         
31230         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31231
31232         if(this.isDocument){
31233             this.setThumbBoxSize();
31234             this.setThumbBoxPosition();
31235             this.setCanvasPosition();
31236         }
31237         
31238         this.draw();
31239         
31240         this.fireEvent('rotate', this, 'right');
31241     },
31242     
31243     onRotateFail : function()
31244     {
31245         this.errorEl.show(true);
31246         
31247         var _this = this;
31248         
31249         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31250     },
31251     
31252     draw : function()
31253     {
31254         this.previewEl.dom.innerHTML = '';
31255         
31256         var canvasEl = document.createElement("canvas");
31257         
31258         var contextEl = canvasEl.getContext("2d");
31259         
31260         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31261         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31262         var center = this.imageEl.OriginWidth / 2;
31263         
31264         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31265             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31266             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31267             center = this.imageEl.OriginHeight / 2;
31268         }
31269         
31270         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31271         
31272         contextEl.translate(center, center);
31273         contextEl.rotate(this.rotate * Math.PI / 180);
31274
31275         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31276         
31277         this.canvasEl = document.createElement("canvas");
31278         
31279         this.contextEl = this.canvasEl.getContext("2d");
31280         
31281         switch (this.rotate) {
31282             case 0 :
31283                 
31284                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31285                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31286                 
31287                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31288                 
31289                 break;
31290             case 90 : 
31291                 
31292                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31293                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31294                 
31295                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31296                     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);
31297                     break;
31298                 }
31299                 
31300                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31301                 
31302                 break;
31303             case 180 :
31304                 
31305                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31306                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31307                 
31308                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31309                     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);
31310                     break;
31311                 }
31312                 
31313                 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);
31314                 
31315                 break;
31316             case 270 :
31317                 
31318                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31319                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31320         
31321                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31322                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31323                     break;
31324                 }
31325                 
31326                 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);
31327                 
31328                 break;
31329             default : 
31330                 break;
31331         }
31332         
31333         this.previewEl.appendChild(this.canvasEl);
31334         
31335         this.setCanvasPosition();
31336     },
31337     
31338     crop : function()
31339     {
31340         if(!this.canvasLoaded){
31341             return;
31342         }
31343         
31344         var imageCanvas = document.createElement("canvas");
31345         
31346         var imageContext = imageCanvas.getContext("2d");
31347         
31348         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31349         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31350         
31351         var center = imageCanvas.width / 2;
31352         
31353         imageContext.translate(center, center);
31354         
31355         imageContext.rotate(this.rotate * Math.PI / 180);
31356         
31357         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31358         
31359         var canvas = document.createElement("canvas");
31360         
31361         var context = canvas.getContext("2d");
31362                 
31363         canvas.width = this.minWidth;
31364         canvas.height = this.minHeight;
31365
31366         switch (this.rotate) {
31367             case 0 :
31368                 
31369                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31370                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31371                 
31372                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31373                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31374                 
31375                 var targetWidth = this.minWidth - 2 * x;
31376                 var targetHeight = this.minHeight - 2 * y;
31377                 
31378                 var scale = 1;
31379                 
31380                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31381                     scale = targetWidth / width;
31382                 }
31383                 
31384                 if(x > 0 && y == 0){
31385                     scale = targetHeight / height;
31386                 }
31387                 
31388                 if(x > 0 && y > 0){
31389                     scale = targetWidth / width;
31390                     
31391                     if(width < height){
31392                         scale = targetHeight / height;
31393                     }
31394                 }
31395                 
31396                 context.scale(scale, scale);
31397                 
31398                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31399                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31400
31401                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31402                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31403
31404                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31405                 
31406                 break;
31407             case 90 : 
31408                 
31409                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31410                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31411                 
31412                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31413                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31414                 
31415                 var targetWidth = this.minWidth - 2 * x;
31416                 var targetHeight = this.minHeight - 2 * y;
31417                 
31418                 var scale = 1;
31419                 
31420                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31421                     scale = targetWidth / width;
31422                 }
31423                 
31424                 if(x > 0 && y == 0){
31425                     scale = targetHeight / height;
31426                 }
31427                 
31428                 if(x > 0 && y > 0){
31429                     scale = targetWidth / width;
31430                     
31431                     if(width < height){
31432                         scale = targetHeight / height;
31433                     }
31434                 }
31435                 
31436                 context.scale(scale, scale);
31437                 
31438                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31439                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31440
31441                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31442                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31443                 
31444                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31445                 
31446                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31447                 
31448                 break;
31449             case 180 :
31450                 
31451                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31452                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31453                 
31454                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31455                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31456                 
31457                 var targetWidth = this.minWidth - 2 * x;
31458                 var targetHeight = this.minHeight - 2 * y;
31459                 
31460                 var scale = 1;
31461                 
31462                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31463                     scale = targetWidth / width;
31464                 }
31465                 
31466                 if(x > 0 && y == 0){
31467                     scale = targetHeight / height;
31468                 }
31469                 
31470                 if(x > 0 && y > 0){
31471                     scale = targetWidth / width;
31472                     
31473                     if(width < height){
31474                         scale = targetHeight / height;
31475                     }
31476                 }
31477                 
31478                 context.scale(scale, scale);
31479                 
31480                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31481                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31482
31483                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31484                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31485
31486                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31487                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31488                 
31489                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31490                 
31491                 break;
31492             case 270 :
31493                 
31494                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31495                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31496                 
31497                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31498                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31499                 
31500                 var targetWidth = this.minWidth - 2 * x;
31501                 var targetHeight = this.minHeight - 2 * y;
31502                 
31503                 var scale = 1;
31504                 
31505                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31506                     scale = targetWidth / width;
31507                 }
31508                 
31509                 if(x > 0 && y == 0){
31510                     scale = targetHeight / height;
31511                 }
31512                 
31513                 if(x > 0 && y > 0){
31514                     scale = targetWidth / width;
31515                     
31516                     if(width < height){
31517                         scale = targetHeight / height;
31518                     }
31519                 }
31520                 
31521                 context.scale(scale, scale);
31522                 
31523                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31524                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31525
31526                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31527                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31528                 
31529                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31530                 
31531                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31532                 
31533                 break;
31534             default : 
31535                 break;
31536         }
31537         
31538         this.cropData = canvas.toDataURL(this.cropType);
31539         
31540         if(this.fireEvent('crop', this, this.cropData) !== false){
31541             this.process(this.file, this.cropData);
31542         }
31543         
31544         return;
31545         
31546     },
31547     
31548     setThumbBoxSize : function()
31549     {
31550         var width, height;
31551         
31552         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31553             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31554             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31555             
31556             this.minWidth = width;
31557             this.minHeight = height;
31558             
31559             if(this.rotate == 90 || this.rotate == 270){
31560                 this.minWidth = height;
31561                 this.minHeight = width;
31562             }
31563         }
31564         
31565         height = 300;
31566         width = Math.ceil(this.minWidth * height / this.minHeight);
31567         
31568         if(this.minWidth > this.minHeight){
31569             width = 300;
31570             height = Math.ceil(this.minHeight * width / this.minWidth);
31571         }
31572         
31573         this.thumbEl.setStyle({
31574             width : width + 'px',
31575             height : height + 'px'
31576         });
31577
31578         return;
31579             
31580     },
31581     
31582     setThumbBoxPosition : function()
31583     {
31584         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31585         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31586         
31587         this.thumbEl.setLeft(x);
31588         this.thumbEl.setTop(y);
31589         
31590     },
31591     
31592     baseRotateLevel : function()
31593     {
31594         this.baseRotate = 1;
31595         
31596         if(
31597                 typeof(this.exif) != 'undefined' &&
31598                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31599                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31600         ){
31601             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31602         }
31603         
31604         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31605         
31606     },
31607     
31608     baseScaleLevel : function()
31609     {
31610         var width, height;
31611         
31612         if(this.isDocument){
31613             
31614             if(this.baseRotate == 6 || this.baseRotate == 8){
31615             
31616                 height = this.thumbEl.getHeight();
31617                 this.baseScale = height / this.imageEl.OriginWidth;
31618
31619                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31620                     width = this.thumbEl.getWidth();
31621                     this.baseScale = width / this.imageEl.OriginHeight;
31622                 }
31623
31624                 return;
31625             }
31626
31627             height = this.thumbEl.getHeight();
31628             this.baseScale = height / this.imageEl.OriginHeight;
31629
31630             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31631                 width = this.thumbEl.getWidth();
31632                 this.baseScale = width / this.imageEl.OriginWidth;
31633             }
31634
31635             return;
31636         }
31637         
31638         if(this.baseRotate == 6 || this.baseRotate == 8){
31639             
31640             width = this.thumbEl.getHeight();
31641             this.baseScale = width / this.imageEl.OriginHeight;
31642             
31643             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31644                 height = this.thumbEl.getWidth();
31645                 this.baseScale = height / this.imageEl.OriginHeight;
31646             }
31647             
31648             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31649                 height = this.thumbEl.getWidth();
31650                 this.baseScale = height / this.imageEl.OriginHeight;
31651                 
31652                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31653                     width = this.thumbEl.getHeight();
31654                     this.baseScale = width / this.imageEl.OriginWidth;
31655                 }
31656             }
31657             
31658             return;
31659         }
31660         
31661         width = this.thumbEl.getWidth();
31662         this.baseScale = width / this.imageEl.OriginWidth;
31663         
31664         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31665             height = this.thumbEl.getHeight();
31666             this.baseScale = height / this.imageEl.OriginHeight;
31667         }
31668         
31669         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31670             
31671             height = this.thumbEl.getHeight();
31672             this.baseScale = height / this.imageEl.OriginHeight;
31673             
31674             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31675                 width = this.thumbEl.getWidth();
31676                 this.baseScale = width / this.imageEl.OriginWidth;
31677             }
31678             
31679         }
31680         
31681         return;
31682     },
31683     
31684     getScaleLevel : function()
31685     {
31686         return this.baseScale * Math.pow(1.1, this.scale);
31687     },
31688     
31689     onTouchStart : function(e)
31690     {
31691         if(!this.canvasLoaded){
31692             this.beforeSelectFile(e);
31693             return;
31694         }
31695         
31696         var touches = e.browserEvent.touches;
31697         
31698         if(!touches){
31699             return;
31700         }
31701         
31702         if(touches.length == 1){
31703             this.onMouseDown(e);
31704             return;
31705         }
31706         
31707         if(touches.length != 2){
31708             return;
31709         }
31710         
31711         var coords = [];
31712         
31713         for(var i = 0, finger; finger = touches[i]; i++){
31714             coords.push(finger.pageX, finger.pageY);
31715         }
31716         
31717         var x = Math.pow(coords[0] - coords[2], 2);
31718         var y = Math.pow(coords[1] - coords[3], 2);
31719         
31720         this.startDistance = Math.sqrt(x + y);
31721         
31722         this.startScale = this.scale;
31723         
31724         this.pinching = true;
31725         this.dragable = false;
31726         
31727     },
31728     
31729     onTouchMove : function(e)
31730     {
31731         if(!this.pinching && !this.dragable){
31732             return;
31733         }
31734         
31735         var touches = e.browserEvent.touches;
31736         
31737         if(!touches){
31738             return;
31739         }
31740         
31741         if(this.dragable){
31742             this.onMouseMove(e);
31743             return;
31744         }
31745         
31746         var coords = [];
31747         
31748         for(var i = 0, finger; finger = touches[i]; i++){
31749             coords.push(finger.pageX, finger.pageY);
31750         }
31751         
31752         var x = Math.pow(coords[0] - coords[2], 2);
31753         var y = Math.pow(coords[1] - coords[3], 2);
31754         
31755         this.endDistance = Math.sqrt(x + y);
31756         
31757         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31758         
31759         if(!this.zoomable()){
31760             this.scale = this.startScale;
31761             return;
31762         }
31763         
31764         this.draw();
31765         
31766     },
31767     
31768     onTouchEnd : function(e)
31769     {
31770         this.pinching = false;
31771         this.dragable = false;
31772         
31773     },
31774     
31775     process : function(file, crop)
31776     {
31777         if(this.loadMask){
31778             this.maskEl.mask(this.loadingText);
31779         }
31780         
31781         this.xhr = new XMLHttpRequest();
31782         
31783         file.xhr = this.xhr;
31784
31785         this.xhr.open(this.method, this.url, true);
31786         
31787         var headers = {
31788             "Accept": "application/json",
31789             "Cache-Control": "no-cache",
31790             "X-Requested-With": "XMLHttpRequest"
31791         };
31792         
31793         for (var headerName in headers) {
31794             var headerValue = headers[headerName];
31795             if (headerValue) {
31796                 this.xhr.setRequestHeader(headerName, headerValue);
31797             }
31798         }
31799         
31800         var _this = this;
31801         
31802         this.xhr.onload = function()
31803         {
31804             _this.xhrOnLoad(_this.xhr);
31805         }
31806         
31807         this.xhr.onerror = function()
31808         {
31809             _this.xhrOnError(_this.xhr);
31810         }
31811         
31812         var formData = new FormData();
31813
31814         formData.append('returnHTML', 'NO');
31815         
31816         if(crop){
31817             formData.append('crop', crop);
31818         }
31819         
31820         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31821             formData.append(this.paramName, file, file.name);
31822         }
31823         
31824         if(typeof(file.filename) != 'undefined'){
31825             formData.append('filename', file.filename);
31826         }
31827         
31828         if(typeof(file.mimetype) != 'undefined'){
31829             formData.append('mimetype', file.mimetype);
31830         }
31831         
31832         if(this.fireEvent('arrange', this, formData) != false){
31833             this.xhr.send(formData);
31834         };
31835     },
31836     
31837     xhrOnLoad : function(xhr)
31838     {
31839         if(this.loadMask){
31840             this.maskEl.unmask();
31841         }
31842         
31843         if (xhr.readyState !== 4) {
31844             this.fireEvent('exception', this, xhr);
31845             return;
31846         }
31847
31848         var response = Roo.decode(xhr.responseText);
31849         
31850         if(!response.success){
31851             this.fireEvent('exception', this, xhr);
31852             return;
31853         }
31854         
31855         var response = Roo.decode(xhr.responseText);
31856         
31857         this.fireEvent('upload', this, response);
31858         
31859     },
31860     
31861     xhrOnError : function()
31862     {
31863         if(this.loadMask){
31864             this.maskEl.unmask();
31865         }
31866         
31867         Roo.log('xhr on error');
31868         
31869         var response = Roo.decode(xhr.responseText);
31870           
31871         Roo.log(response);
31872         
31873     },
31874     
31875     prepare : function(file)
31876     {   
31877         if(this.loadMask){
31878             this.maskEl.mask(this.loadingText);
31879         }
31880         
31881         this.file = false;
31882         this.exif = {};
31883         
31884         if(typeof(file) === 'string'){
31885             this.loadCanvas(file);
31886             return;
31887         }
31888         
31889         if(!file || !this.urlAPI){
31890             return;
31891         }
31892         
31893         this.file = file;
31894         this.cropType = file.type;
31895         
31896         var _this = this;
31897         
31898         if(this.fireEvent('prepare', this, this.file) != false){
31899             
31900             var reader = new FileReader();
31901             
31902             reader.onload = function (e) {
31903                 if (e.target.error) {
31904                     Roo.log(e.target.error);
31905                     return;
31906                 }
31907                 
31908                 var buffer = e.target.result,
31909                     dataView = new DataView(buffer),
31910                     offset = 2,
31911                     maxOffset = dataView.byteLength - 4,
31912                     markerBytes,
31913                     markerLength;
31914                 
31915                 if (dataView.getUint16(0) === 0xffd8) {
31916                     while (offset < maxOffset) {
31917                         markerBytes = dataView.getUint16(offset);
31918                         
31919                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31920                             markerLength = dataView.getUint16(offset + 2) + 2;
31921                             if (offset + markerLength > dataView.byteLength) {
31922                                 Roo.log('Invalid meta data: Invalid segment size.');
31923                                 break;
31924                             }
31925                             
31926                             if(markerBytes == 0xffe1){
31927                                 _this.parseExifData(
31928                                     dataView,
31929                                     offset,
31930                                     markerLength
31931                                 );
31932                             }
31933                             
31934                             offset += markerLength;
31935                             
31936                             continue;
31937                         }
31938                         
31939                         break;
31940                     }
31941                     
31942                 }
31943                 
31944                 var url = _this.urlAPI.createObjectURL(_this.file);
31945                 
31946                 _this.loadCanvas(url);
31947                 
31948                 return;
31949             }
31950             
31951             reader.readAsArrayBuffer(this.file);
31952             
31953         }
31954         
31955     },
31956     
31957     parseExifData : function(dataView, offset, length)
31958     {
31959         var tiffOffset = offset + 10,
31960             littleEndian,
31961             dirOffset;
31962     
31963         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31964             // No Exif data, might be XMP data instead
31965             return;
31966         }
31967         
31968         // Check for the ASCII code for "Exif" (0x45786966):
31969         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31970             // No Exif data, might be XMP data instead
31971             return;
31972         }
31973         if (tiffOffset + 8 > dataView.byteLength) {
31974             Roo.log('Invalid Exif data: Invalid segment size.');
31975             return;
31976         }
31977         // Check for the two null bytes:
31978         if (dataView.getUint16(offset + 8) !== 0x0000) {
31979             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31980             return;
31981         }
31982         // Check the byte alignment:
31983         switch (dataView.getUint16(tiffOffset)) {
31984         case 0x4949:
31985             littleEndian = true;
31986             break;
31987         case 0x4D4D:
31988             littleEndian = false;
31989             break;
31990         default:
31991             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31992             return;
31993         }
31994         // Check for the TIFF tag marker (0x002A):
31995         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31996             Roo.log('Invalid Exif data: Missing TIFF marker.');
31997             return;
31998         }
31999         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32000         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32001         
32002         this.parseExifTags(
32003             dataView,
32004             tiffOffset,
32005             tiffOffset + dirOffset,
32006             littleEndian
32007         );
32008     },
32009     
32010     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32011     {
32012         var tagsNumber,
32013             dirEndOffset,
32014             i;
32015         if (dirOffset + 6 > dataView.byteLength) {
32016             Roo.log('Invalid Exif data: Invalid directory offset.');
32017             return;
32018         }
32019         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32020         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32021         if (dirEndOffset + 4 > dataView.byteLength) {
32022             Roo.log('Invalid Exif data: Invalid directory size.');
32023             return;
32024         }
32025         for (i = 0; i < tagsNumber; i += 1) {
32026             this.parseExifTag(
32027                 dataView,
32028                 tiffOffset,
32029                 dirOffset + 2 + 12 * i, // tag offset
32030                 littleEndian
32031             );
32032         }
32033         // Return the offset to the next directory:
32034         return dataView.getUint32(dirEndOffset, littleEndian);
32035     },
32036     
32037     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32038     {
32039         var tag = dataView.getUint16(offset, littleEndian);
32040         
32041         this.exif[tag] = this.getExifValue(
32042             dataView,
32043             tiffOffset,
32044             offset,
32045             dataView.getUint16(offset + 2, littleEndian), // tag type
32046             dataView.getUint32(offset + 4, littleEndian), // tag length
32047             littleEndian
32048         );
32049     },
32050     
32051     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32052     {
32053         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32054             tagSize,
32055             dataOffset,
32056             values,
32057             i,
32058             str,
32059             c;
32060     
32061         if (!tagType) {
32062             Roo.log('Invalid Exif data: Invalid tag type.');
32063             return;
32064         }
32065         
32066         tagSize = tagType.size * length;
32067         // Determine if the value is contained in the dataOffset bytes,
32068         // or if the value at the dataOffset is a pointer to the actual data:
32069         dataOffset = tagSize > 4 ?
32070                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32071         if (dataOffset + tagSize > dataView.byteLength) {
32072             Roo.log('Invalid Exif data: Invalid data offset.');
32073             return;
32074         }
32075         if (length === 1) {
32076             return tagType.getValue(dataView, dataOffset, littleEndian);
32077         }
32078         values = [];
32079         for (i = 0; i < length; i += 1) {
32080             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32081         }
32082         
32083         if (tagType.ascii) {
32084             str = '';
32085             // Concatenate the chars:
32086             for (i = 0; i < values.length; i += 1) {
32087                 c = values[i];
32088                 // Ignore the terminating NULL byte(s):
32089                 if (c === '\u0000') {
32090                     break;
32091                 }
32092                 str += c;
32093             }
32094             return str;
32095         }
32096         return values;
32097     }
32098     
32099 });
32100
32101 Roo.apply(Roo.bootstrap.UploadCropbox, {
32102     tags : {
32103         'Orientation': 0x0112
32104     },
32105     
32106     Orientation: {
32107             1: 0, //'top-left',
32108 //            2: 'top-right',
32109             3: 180, //'bottom-right',
32110 //            4: 'bottom-left',
32111 //            5: 'left-top',
32112             6: 90, //'right-top',
32113 //            7: 'right-bottom',
32114             8: 270 //'left-bottom'
32115     },
32116     
32117     exifTagTypes : {
32118         // byte, 8-bit unsigned int:
32119         1: {
32120             getValue: function (dataView, dataOffset) {
32121                 return dataView.getUint8(dataOffset);
32122             },
32123             size: 1
32124         },
32125         // ascii, 8-bit byte:
32126         2: {
32127             getValue: function (dataView, dataOffset) {
32128                 return String.fromCharCode(dataView.getUint8(dataOffset));
32129             },
32130             size: 1,
32131             ascii: true
32132         },
32133         // short, 16 bit int:
32134         3: {
32135             getValue: function (dataView, dataOffset, littleEndian) {
32136                 return dataView.getUint16(dataOffset, littleEndian);
32137             },
32138             size: 2
32139         },
32140         // long, 32 bit int:
32141         4: {
32142             getValue: function (dataView, dataOffset, littleEndian) {
32143                 return dataView.getUint32(dataOffset, littleEndian);
32144             },
32145             size: 4
32146         },
32147         // rational = two long values, first is numerator, second is denominator:
32148         5: {
32149             getValue: function (dataView, dataOffset, littleEndian) {
32150                 return dataView.getUint32(dataOffset, littleEndian) /
32151                     dataView.getUint32(dataOffset + 4, littleEndian);
32152             },
32153             size: 8
32154         },
32155         // slong, 32 bit signed int:
32156         9: {
32157             getValue: function (dataView, dataOffset, littleEndian) {
32158                 return dataView.getInt32(dataOffset, littleEndian);
32159             },
32160             size: 4
32161         },
32162         // srational, two slongs, first is numerator, second is denominator:
32163         10: {
32164             getValue: function (dataView, dataOffset, littleEndian) {
32165                 return dataView.getInt32(dataOffset, littleEndian) /
32166                     dataView.getInt32(dataOffset + 4, littleEndian);
32167             },
32168             size: 8
32169         }
32170     },
32171     
32172     footer : {
32173         STANDARD : [
32174             {
32175                 tag : 'div',
32176                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32177                 action : 'rotate-left',
32178                 cn : [
32179                     {
32180                         tag : 'button',
32181                         cls : 'btn btn-default',
32182                         html : '<i class="fa fa-undo"></i>'
32183                     }
32184                 ]
32185             },
32186             {
32187                 tag : 'div',
32188                 cls : 'btn-group roo-upload-cropbox-picture',
32189                 action : 'picture',
32190                 cn : [
32191                     {
32192                         tag : 'button',
32193                         cls : 'btn btn-default',
32194                         html : '<i class="fa fa-picture-o"></i>'
32195                     }
32196                 ]
32197             },
32198             {
32199                 tag : 'div',
32200                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32201                 action : 'rotate-right',
32202                 cn : [
32203                     {
32204                         tag : 'button',
32205                         cls : 'btn btn-default',
32206                         html : '<i class="fa fa-repeat"></i>'
32207                     }
32208                 ]
32209             }
32210         ],
32211         DOCUMENT : [
32212             {
32213                 tag : 'div',
32214                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32215                 action : 'rotate-left',
32216                 cn : [
32217                     {
32218                         tag : 'button',
32219                         cls : 'btn btn-default',
32220                         html : '<i class="fa fa-undo"></i>'
32221                     }
32222                 ]
32223             },
32224             {
32225                 tag : 'div',
32226                 cls : 'btn-group roo-upload-cropbox-download',
32227                 action : 'download',
32228                 cn : [
32229                     {
32230                         tag : 'button',
32231                         cls : 'btn btn-default',
32232                         html : '<i class="fa fa-download"></i>'
32233                     }
32234                 ]
32235             },
32236             {
32237                 tag : 'div',
32238                 cls : 'btn-group roo-upload-cropbox-crop',
32239                 action : 'crop',
32240                 cn : [
32241                     {
32242                         tag : 'button',
32243                         cls : 'btn btn-default',
32244                         html : '<i class="fa fa-crop"></i>'
32245                     }
32246                 ]
32247             },
32248             {
32249                 tag : 'div',
32250                 cls : 'btn-group roo-upload-cropbox-trash',
32251                 action : 'trash',
32252                 cn : [
32253                     {
32254                         tag : 'button',
32255                         cls : 'btn btn-default',
32256                         html : '<i class="fa fa-trash"></i>'
32257                     }
32258                 ]
32259             },
32260             {
32261                 tag : 'div',
32262                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32263                 action : 'rotate-right',
32264                 cn : [
32265                     {
32266                         tag : 'button',
32267                         cls : 'btn btn-default',
32268                         html : '<i class="fa fa-repeat"></i>'
32269                     }
32270                 ]
32271             }
32272         ],
32273         ROTATOR : [
32274             {
32275                 tag : 'div',
32276                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32277                 action : 'rotate-left',
32278                 cn : [
32279                     {
32280                         tag : 'button',
32281                         cls : 'btn btn-default',
32282                         html : '<i class="fa fa-undo"></i>'
32283                     }
32284                 ]
32285             },
32286             {
32287                 tag : 'div',
32288                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32289                 action : 'rotate-right',
32290                 cn : [
32291                     {
32292                         tag : 'button',
32293                         cls : 'btn btn-default',
32294                         html : '<i class="fa fa-repeat"></i>'
32295                     }
32296                 ]
32297             }
32298         ]
32299     }
32300 });
32301
32302 /*
32303 * Licence: LGPL
32304 */
32305
32306 /**
32307  * @class Roo.bootstrap.DocumentManager
32308  * @extends Roo.bootstrap.Component
32309  * Bootstrap DocumentManager class
32310  * @cfg {String} paramName default 'imageUpload'
32311  * @cfg {String} toolTipName default 'filename'
32312  * @cfg {String} method default POST
32313  * @cfg {String} url action url
32314  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32315  * @cfg {Boolean} multiple multiple upload default true
32316  * @cfg {Number} thumbSize default 300
32317  * @cfg {String} fieldLabel
32318  * @cfg {Number} labelWidth default 4
32319  * @cfg {String} labelAlign (left|top) default left
32320  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32321 * @cfg {Number} labellg set the width of label (1-12)
32322  * @cfg {Number} labelmd set the width of label (1-12)
32323  * @cfg {Number} labelsm set the width of label (1-12)
32324  * @cfg {Number} labelxs set the width of label (1-12)
32325  * 
32326  * @constructor
32327  * Create a new DocumentManager
32328  * @param {Object} config The config object
32329  */
32330
32331 Roo.bootstrap.DocumentManager = function(config){
32332     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32333     
32334     this.files = [];
32335     this.delegates = [];
32336     
32337     this.addEvents({
32338         /**
32339          * @event initial
32340          * Fire when initial the DocumentManager
32341          * @param {Roo.bootstrap.DocumentManager} this
32342          */
32343         "initial" : true,
32344         /**
32345          * @event inspect
32346          * inspect selected file
32347          * @param {Roo.bootstrap.DocumentManager} this
32348          * @param {File} file
32349          */
32350         "inspect" : true,
32351         /**
32352          * @event exception
32353          * Fire when xhr load exception
32354          * @param {Roo.bootstrap.DocumentManager} this
32355          * @param {XMLHttpRequest} xhr
32356          */
32357         "exception" : true,
32358         /**
32359          * @event afterupload
32360          * Fire when xhr load exception
32361          * @param {Roo.bootstrap.DocumentManager} this
32362          * @param {XMLHttpRequest} xhr
32363          */
32364         "afterupload" : true,
32365         /**
32366          * @event prepare
32367          * prepare the form data
32368          * @param {Roo.bootstrap.DocumentManager} this
32369          * @param {Object} formData
32370          */
32371         "prepare" : true,
32372         /**
32373          * @event remove
32374          * Fire when remove the file
32375          * @param {Roo.bootstrap.DocumentManager} this
32376          * @param {Object} file
32377          */
32378         "remove" : true,
32379         /**
32380          * @event refresh
32381          * Fire after refresh the file
32382          * @param {Roo.bootstrap.DocumentManager} this
32383          */
32384         "refresh" : true,
32385         /**
32386          * @event click
32387          * Fire after click the image
32388          * @param {Roo.bootstrap.DocumentManager} this
32389          * @param {Object} file
32390          */
32391         "click" : true,
32392         /**
32393          * @event edit
32394          * Fire when upload a image and editable set to true
32395          * @param {Roo.bootstrap.DocumentManager} this
32396          * @param {Object} file
32397          */
32398         "edit" : true,
32399         /**
32400          * @event beforeselectfile
32401          * Fire before select file
32402          * @param {Roo.bootstrap.DocumentManager} this
32403          */
32404         "beforeselectfile" : true,
32405         /**
32406          * @event process
32407          * Fire before process file
32408          * @param {Roo.bootstrap.DocumentManager} this
32409          * @param {Object} file
32410          */
32411         "process" : true,
32412         /**
32413          * @event previewrendered
32414          * Fire when preview rendered
32415          * @param {Roo.bootstrap.DocumentManager} this
32416          * @param {Object} file
32417          */
32418         "previewrendered" : true,
32419         /**
32420          */
32421         "previewResize" : true
32422         
32423     });
32424 };
32425
32426 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32427     
32428     boxes : 0,
32429     inputName : '',
32430     thumbSize : 300,
32431     multiple : true,
32432     files : false,
32433     method : 'POST',
32434     url : '',
32435     paramName : 'imageUpload',
32436     toolTipName : 'filename',
32437     fieldLabel : '',
32438     labelWidth : 4,
32439     labelAlign : 'left',
32440     editable : true,
32441     delegates : false,
32442     xhr : false, 
32443     
32444     labellg : 0,
32445     labelmd : 0,
32446     labelsm : 0,
32447     labelxs : 0,
32448     
32449     getAutoCreate : function()
32450     {   
32451         var managerWidget = {
32452             tag : 'div',
32453             cls : 'roo-document-manager',
32454             cn : [
32455                 {
32456                     tag : 'input',
32457                     cls : 'roo-document-manager-selector',
32458                     type : 'file'
32459                 },
32460                 {
32461                     tag : 'div',
32462                     cls : 'roo-document-manager-uploader',
32463                     cn : [
32464                         {
32465                             tag : 'div',
32466                             cls : 'roo-document-manager-upload-btn',
32467                             html : '<i class="fa fa-plus"></i>'
32468                         }
32469                     ]
32470                     
32471                 }
32472             ]
32473         };
32474         
32475         var content = [
32476             {
32477                 tag : 'div',
32478                 cls : 'column col-md-12',
32479                 cn : managerWidget
32480             }
32481         ];
32482         
32483         if(this.fieldLabel.length){
32484             
32485             content = [
32486                 {
32487                     tag : 'div',
32488                     cls : 'column col-md-12',
32489                     html : this.fieldLabel
32490                 },
32491                 {
32492                     tag : 'div',
32493                     cls : 'column col-md-12',
32494                     cn : managerWidget
32495                 }
32496             ];
32497
32498             if(this.labelAlign == 'left'){
32499                 content = [
32500                     {
32501                         tag : 'div',
32502                         cls : 'column',
32503                         html : this.fieldLabel
32504                     },
32505                     {
32506                         tag : 'div',
32507                         cls : 'column',
32508                         cn : managerWidget
32509                     }
32510                 ];
32511                 
32512                 if(this.labelWidth > 12){
32513                     content[0].style = "width: " + this.labelWidth + 'px';
32514                 }
32515
32516                 if(this.labelWidth < 13 && this.labelmd == 0){
32517                     this.labelmd = this.labelWidth;
32518                 }
32519
32520                 if(this.labellg > 0){
32521                     content[0].cls += ' col-lg-' + this.labellg;
32522                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32523                 }
32524
32525                 if(this.labelmd > 0){
32526                     content[0].cls += ' col-md-' + this.labelmd;
32527                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32528                 }
32529
32530                 if(this.labelsm > 0){
32531                     content[0].cls += ' col-sm-' + this.labelsm;
32532                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32533                 }
32534
32535                 if(this.labelxs > 0){
32536                     content[0].cls += ' col-xs-' + this.labelxs;
32537                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32538                 }
32539                 
32540             }
32541         }
32542         
32543         var cfg = {
32544             tag : 'div',
32545             cls : 'row clearfix',
32546             cn : content
32547         };
32548         
32549         return cfg;
32550         
32551     },
32552     
32553     initEvents : function()
32554     {
32555         this.managerEl = this.el.select('.roo-document-manager', true).first();
32556         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32557         
32558         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32559         this.selectorEl.hide();
32560         
32561         if(this.multiple){
32562             this.selectorEl.attr('multiple', 'multiple');
32563         }
32564         
32565         this.selectorEl.on('change', this.onFileSelected, this);
32566         
32567         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32568         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32569         
32570         this.uploader.on('click', this.onUploaderClick, this);
32571         
32572         this.renderProgressDialog();
32573         
32574         var _this = this;
32575         
32576         window.addEventListener("resize", function() { _this.refresh(); } );
32577         
32578         this.fireEvent('initial', this);
32579     },
32580     
32581     renderProgressDialog : function()
32582     {
32583         var _this = this;
32584         
32585         this.progressDialog = new Roo.bootstrap.Modal({
32586             cls : 'roo-document-manager-progress-dialog',
32587             allow_close : false,
32588             animate : false,
32589             title : '',
32590             buttons : [
32591                 {
32592                     name  :'cancel',
32593                     weight : 'danger',
32594                     html : 'Cancel'
32595                 }
32596             ], 
32597             listeners : { 
32598                 btnclick : function() {
32599                     _this.uploadCancel();
32600                     this.hide();
32601                 }
32602             }
32603         });
32604          
32605         this.progressDialog.render(Roo.get(document.body));
32606          
32607         this.progress = new Roo.bootstrap.Progress({
32608             cls : 'roo-document-manager-progress',
32609             active : true,
32610             striped : true
32611         });
32612         
32613         this.progress.render(this.progressDialog.getChildContainer());
32614         
32615         this.progressBar = new Roo.bootstrap.ProgressBar({
32616             cls : 'roo-document-manager-progress-bar',
32617             aria_valuenow : 0,
32618             aria_valuemin : 0,
32619             aria_valuemax : 12,
32620             panel : 'success'
32621         });
32622         
32623         this.progressBar.render(this.progress.getChildContainer());
32624     },
32625     
32626     onUploaderClick : function(e)
32627     {
32628         e.preventDefault();
32629      
32630         if(this.fireEvent('beforeselectfile', this) != false){
32631             this.selectorEl.dom.click();
32632         }
32633         
32634     },
32635     
32636     onFileSelected : function(e)
32637     {
32638         e.preventDefault();
32639         
32640         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32641             return;
32642         }
32643         
32644         Roo.each(this.selectorEl.dom.files, function(file){
32645             if(this.fireEvent('inspect', this, file) != false){
32646                 this.files.push(file);
32647             }
32648         }, this);
32649         
32650         this.queue();
32651         
32652     },
32653     
32654     queue : function()
32655     {
32656         this.selectorEl.dom.value = '';
32657         
32658         if(!this.files || !this.files.length){
32659             return;
32660         }
32661         
32662         if(this.boxes > 0 && this.files.length > this.boxes){
32663             this.files = this.files.slice(0, this.boxes);
32664         }
32665         
32666         this.uploader.show();
32667         
32668         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32669             this.uploader.hide();
32670         }
32671         
32672         var _this = this;
32673         
32674         var files = [];
32675         
32676         var docs = [];
32677         
32678         Roo.each(this.files, function(file){
32679             
32680             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32681                 var f = this.renderPreview(file);
32682                 files.push(f);
32683                 return;
32684             }
32685             
32686             if(file.type.indexOf('image') != -1){
32687                 this.delegates.push(
32688                     (function(){
32689                         _this.process(file);
32690                     }).createDelegate(this)
32691                 );
32692         
32693                 return;
32694             }
32695             
32696             docs.push(
32697                 (function(){
32698                     _this.process(file);
32699                 }).createDelegate(this)
32700             );
32701             
32702         }, this);
32703         
32704         this.files = files;
32705         
32706         this.delegates = this.delegates.concat(docs);
32707         
32708         if(!this.delegates.length){
32709             this.refresh();
32710             return;
32711         }
32712         
32713         this.progressBar.aria_valuemax = this.delegates.length;
32714         
32715         this.arrange();
32716         
32717         return;
32718     },
32719     
32720     arrange : function()
32721     {
32722         if(!this.delegates.length){
32723             this.progressDialog.hide();
32724             this.refresh();
32725             return;
32726         }
32727         
32728         var delegate = this.delegates.shift();
32729         
32730         this.progressDialog.show();
32731         
32732         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32733         
32734         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32735         
32736         delegate();
32737     },
32738     
32739     refresh : function()
32740     {
32741         this.uploader.show();
32742         
32743         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32744             this.uploader.hide();
32745         }
32746         
32747         Roo.isTouch ? this.closable(false) : this.closable(true);
32748         
32749         this.fireEvent('refresh', this);
32750     },
32751     
32752     onRemove : function(e, el, o)
32753     {
32754         e.preventDefault();
32755         
32756         this.fireEvent('remove', this, o);
32757         
32758     },
32759     
32760     remove : function(o)
32761     {
32762         var files = [];
32763         
32764         Roo.each(this.files, function(file){
32765             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32766                 files.push(file);
32767                 return;
32768             }
32769
32770             o.target.remove();
32771
32772         }, this);
32773         
32774         this.files = files;
32775         
32776         this.refresh();
32777     },
32778     
32779     clear : function()
32780     {
32781         Roo.each(this.files, function(file){
32782             if(!file.target){
32783                 return;
32784             }
32785             
32786             file.target.remove();
32787
32788         }, this);
32789         
32790         this.files = [];
32791         
32792         this.refresh();
32793     },
32794     
32795     onClick : function(e, el, o)
32796     {
32797         e.preventDefault();
32798         
32799         this.fireEvent('click', this, o);
32800         
32801     },
32802     
32803     closable : function(closable)
32804     {
32805         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32806             
32807             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32808             
32809             if(closable){
32810                 el.show();
32811                 return;
32812             }
32813             
32814             el.hide();
32815             
32816         }, this);
32817     },
32818     
32819     xhrOnLoad : function(xhr)
32820     {
32821         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32822             el.remove();
32823         }, this);
32824         
32825         if (xhr.readyState !== 4) {
32826             this.arrange();
32827             this.fireEvent('exception', this, xhr);
32828             return;
32829         }
32830
32831         var response = Roo.decode(xhr.responseText);
32832         
32833         if(!response.success){
32834             this.arrange();
32835             this.fireEvent('exception', this, xhr);
32836             return;
32837         }
32838         
32839         var file = this.renderPreview(response.data);
32840         
32841         this.files.push(file);
32842         
32843         this.arrange();
32844         
32845         this.fireEvent('afterupload', this, xhr);
32846         
32847     },
32848     
32849     xhrOnError : function(xhr)
32850     {
32851         Roo.log('xhr on error');
32852         
32853         var response = Roo.decode(xhr.responseText);
32854           
32855         Roo.log(response);
32856         
32857         this.arrange();
32858     },
32859     
32860     process : function(file)
32861     {
32862         if(this.fireEvent('process', this, file) !== false){
32863             if(this.editable && file.type.indexOf('image') != -1){
32864                 this.fireEvent('edit', this, file);
32865                 return;
32866             }
32867
32868             this.uploadStart(file, false);
32869
32870             return;
32871         }
32872         
32873     },
32874     
32875     uploadStart : function(file, crop)
32876     {
32877         this.xhr = new XMLHttpRequest();
32878         
32879         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32880             this.arrange();
32881             return;
32882         }
32883         
32884         file.xhr = this.xhr;
32885             
32886         this.managerEl.createChild({
32887             tag : 'div',
32888             cls : 'roo-document-manager-loading',
32889             cn : [
32890                 {
32891                     tag : 'div',
32892                     tooltip : file.name,
32893                     cls : 'roo-document-manager-thumb',
32894                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32895                 }
32896             ]
32897
32898         });
32899
32900         this.xhr.open(this.method, this.url, true);
32901         
32902         var headers = {
32903             "Accept": "application/json",
32904             "Cache-Control": "no-cache",
32905             "X-Requested-With": "XMLHttpRequest"
32906         };
32907         
32908         for (var headerName in headers) {
32909             var headerValue = headers[headerName];
32910             if (headerValue) {
32911                 this.xhr.setRequestHeader(headerName, headerValue);
32912             }
32913         }
32914         
32915         var _this = this;
32916         
32917         this.xhr.onload = function()
32918         {
32919             _this.xhrOnLoad(_this.xhr);
32920         }
32921         
32922         this.xhr.onerror = function()
32923         {
32924             _this.xhrOnError(_this.xhr);
32925         }
32926         
32927         var formData = new FormData();
32928
32929         formData.append('returnHTML', 'NO');
32930         
32931         if(crop){
32932             formData.append('crop', crop);
32933         }
32934         
32935         formData.append(this.paramName, file, file.name);
32936         
32937         var options = {
32938             file : file, 
32939             manually : false
32940         };
32941         
32942         if(this.fireEvent('prepare', this, formData, options) != false){
32943             
32944             if(options.manually){
32945                 return;
32946             }
32947             
32948             this.xhr.send(formData);
32949             return;
32950         };
32951         
32952         this.uploadCancel();
32953     },
32954     
32955     uploadCancel : function()
32956     {
32957         if (this.xhr) {
32958             this.xhr.abort();
32959         }
32960         
32961         this.delegates = [];
32962         
32963         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32964             el.remove();
32965         }, this);
32966         
32967         this.arrange();
32968     },
32969     
32970     renderPreview : function(file)
32971     {
32972         if(typeof(file.target) != 'undefined' && file.target){
32973             return file;
32974         }
32975         
32976         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32977         
32978         var previewEl = this.managerEl.createChild({
32979             tag : 'div',
32980             cls : 'roo-document-manager-preview',
32981             cn : [
32982                 {
32983                     tag : 'div',
32984                     tooltip : file[this.toolTipName],
32985                     cls : 'roo-document-manager-thumb',
32986                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32987                 },
32988                 {
32989                     tag : 'button',
32990                     cls : 'close',
32991                     html : '<i class="fa fa-times-circle"></i>'
32992                 }
32993             ]
32994         });
32995
32996         var close = previewEl.select('button.close', true).first();
32997
32998         close.on('click', this.onRemove, this, file);
32999
33000         file.target = previewEl;
33001
33002         var image = previewEl.select('img', true).first();
33003         
33004         var _this = this;
33005         
33006         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33007         
33008         image.on('click', this.onClick, this, file);
33009         
33010         this.fireEvent('previewrendered', this, file);
33011         
33012         return file;
33013         
33014     },
33015     
33016     onPreviewLoad : function(file, image)
33017     {
33018         if(typeof(file.target) == 'undefined' || !file.target){
33019             return;
33020         }
33021         
33022         var width = image.dom.naturalWidth || image.dom.width;
33023         var height = image.dom.naturalHeight || image.dom.height;
33024         
33025         if(!this.previewResize) {
33026             return;
33027         }
33028         
33029         if(width > height){
33030             file.target.addClass('wide');
33031             return;
33032         }
33033         
33034         file.target.addClass('tall');
33035         return;
33036         
33037     },
33038     
33039     uploadFromSource : function(file, crop)
33040     {
33041         this.xhr = new XMLHttpRequest();
33042         
33043         this.managerEl.createChild({
33044             tag : 'div',
33045             cls : 'roo-document-manager-loading',
33046             cn : [
33047                 {
33048                     tag : 'div',
33049                     tooltip : file.name,
33050                     cls : 'roo-document-manager-thumb',
33051                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33052                 }
33053             ]
33054
33055         });
33056
33057         this.xhr.open(this.method, this.url, true);
33058         
33059         var headers = {
33060             "Accept": "application/json",
33061             "Cache-Control": "no-cache",
33062             "X-Requested-With": "XMLHttpRequest"
33063         };
33064         
33065         for (var headerName in headers) {
33066             var headerValue = headers[headerName];
33067             if (headerValue) {
33068                 this.xhr.setRequestHeader(headerName, headerValue);
33069             }
33070         }
33071         
33072         var _this = this;
33073         
33074         this.xhr.onload = function()
33075         {
33076             _this.xhrOnLoad(_this.xhr);
33077         }
33078         
33079         this.xhr.onerror = function()
33080         {
33081             _this.xhrOnError(_this.xhr);
33082         }
33083         
33084         var formData = new FormData();
33085
33086         formData.append('returnHTML', 'NO');
33087         
33088         formData.append('crop', crop);
33089         
33090         if(typeof(file.filename) != 'undefined'){
33091             formData.append('filename', file.filename);
33092         }
33093         
33094         if(typeof(file.mimetype) != 'undefined'){
33095             formData.append('mimetype', file.mimetype);
33096         }
33097         
33098         Roo.log(formData);
33099         
33100         if(this.fireEvent('prepare', this, formData) != false){
33101             this.xhr.send(formData);
33102         };
33103     }
33104 });
33105
33106 /*
33107 * Licence: LGPL
33108 */
33109
33110 /**
33111  * @class Roo.bootstrap.DocumentViewer
33112  * @extends Roo.bootstrap.Component
33113  * Bootstrap DocumentViewer class
33114  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33115  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33116  * 
33117  * @constructor
33118  * Create a new DocumentViewer
33119  * @param {Object} config The config object
33120  */
33121
33122 Roo.bootstrap.DocumentViewer = function(config){
33123     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33124     
33125     this.addEvents({
33126         /**
33127          * @event initial
33128          * Fire after initEvent
33129          * @param {Roo.bootstrap.DocumentViewer} this
33130          */
33131         "initial" : true,
33132         /**
33133          * @event click
33134          * Fire after click
33135          * @param {Roo.bootstrap.DocumentViewer} this
33136          */
33137         "click" : true,
33138         /**
33139          * @event download
33140          * Fire after download button
33141          * @param {Roo.bootstrap.DocumentViewer} this
33142          */
33143         "download" : true,
33144         /**
33145          * @event trash
33146          * Fire after trash button
33147          * @param {Roo.bootstrap.DocumentViewer} this
33148          */
33149         "trash" : true
33150         
33151     });
33152 };
33153
33154 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33155     
33156     showDownload : true,
33157     
33158     showTrash : true,
33159     
33160     getAutoCreate : function()
33161     {
33162         var cfg = {
33163             tag : 'div',
33164             cls : 'roo-document-viewer',
33165             cn : [
33166                 {
33167                     tag : 'div',
33168                     cls : 'roo-document-viewer-body',
33169                     cn : [
33170                         {
33171                             tag : 'div',
33172                             cls : 'roo-document-viewer-thumb',
33173                             cn : [
33174                                 {
33175                                     tag : 'img',
33176                                     cls : 'roo-document-viewer-image'
33177                                 }
33178                             ]
33179                         }
33180                     ]
33181                 },
33182                 {
33183                     tag : 'div',
33184                     cls : 'roo-document-viewer-footer',
33185                     cn : {
33186                         tag : 'div',
33187                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33188                         cn : [
33189                             {
33190                                 tag : 'div',
33191                                 cls : 'btn-group roo-document-viewer-download',
33192                                 cn : [
33193                                     {
33194                                         tag : 'button',
33195                                         cls : 'btn btn-default',
33196                                         html : '<i class="fa fa-download"></i>'
33197                                     }
33198                                 ]
33199                             },
33200                             {
33201                                 tag : 'div',
33202                                 cls : 'btn-group roo-document-viewer-trash',
33203                                 cn : [
33204                                     {
33205                                         tag : 'button',
33206                                         cls : 'btn btn-default',
33207                                         html : '<i class="fa fa-trash"></i>'
33208                                     }
33209                                 ]
33210                             }
33211                         ]
33212                     }
33213                 }
33214             ]
33215         };
33216         
33217         return cfg;
33218     },
33219     
33220     initEvents : function()
33221     {
33222         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33223         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33224         
33225         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33226         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33227         
33228         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33229         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33230         
33231         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33232         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33233         
33234         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33235         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33236         
33237         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33238         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33239         
33240         this.bodyEl.on('click', this.onClick, this);
33241         this.downloadBtn.on('click', this.onDownload, this);
33242         this.trashBtn.on('click', this.onTrash, this);
33243         
33244         this.downloadBtn.hide();
33245         this.trashBtn.hide();
33246         
33247         if(this.showDownload){
33248             this.downloadBtn.show();
33249         }
33250         
33251         if(this.showTrash){
33252             this.trashBtn.show();
33253         }
33254         
33255         if(!this.showDownload && !this.showTrash) {
33256             this.footerEl.hide();
33257         }
33258         
33259     },
33260     
33261     initial : function()
33262     {
33263         this.fireEvent('initial', this);
33264         
33265     },
33266     
33267     onClick : function(e)
33268     {
33269         e.preventDefault();
33270         
33271         this.fireEvent('click', this);
33272     },
33273     
33274     onDownload : function(e)
33275     {
33276         e.preventDefault();
33277         
33278         this.fireEvent('download', this);
33279     },
33280     
33281     onTrash : function(e)
33282     {
33283         e.preventDefault();
33284         
33285         this.fireEvent('trash', this);
33286     }
33287     
33288 });
33289 /*
33290  * - LGPL
33291  *
33292  * nav progress bar
33293  * 
33294  */
33295
33296 /**
33297  * @class Roo.bootstrap.NavProgressBar
33298  * @extends Roo.bootstrap.Component
33299  * Bootstrap NavProgressBar class
33300  * 
33301  * @constructor
33302  * Create a new nav progress bar
33303  * @param {Object} config The config object
33304  */
33305
33306 Roo.bootstrap.NavProgressBar = function(config){
33307     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33308
33309     this.bullets = this.bullets || [];
33310    
33311 //    Roo.bootstrap.NavProgressBar.register(this);
33312      this.addEvents({
33313         /**
33314              * @event changed
33315              * Fires when the active item changes
33316              * @param {Roo.bootstrap.NavProgressBar} this
33317              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33318              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33319          */
33320         'changed': true
33321      });
33322     
33323 };
33324
33325 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33326     
33327     bullets : [],
33328     barItems : [],
33329     
33330     getAutoCreate : function()
33331     {
33332         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33333         
33334         cfg = {
33335             tag : 'div',
33336             cls : 'roo-navigation-bar-group',
33337             cn : [
33338                 {
33339                     tag : 'div',
33340                     cls : 'roo-navigation-top-bar'
33341                 },
33342                 {
33343                     tag : 'div',
33344                     cls : 'roo-navigation-bullets-bar',
33345                     cn : [
33346                         {
33347                             tag : 'ul',
33348                             cls : 'roo-navigation-bar'
33349                         }
33350                     ]
33351                 },
33352                 
33353                 {
33354                     tag : 'div',
33355                     cls : 'roo-navigation-bottom-bar'
33356                 }
33357             ]
33358             
33359         };
33360         
33361         return cfg;
33362         
33363     },
33364     
33365     initEvents: function() 
33366     {
33367         
33368     },
33369     
33370     onRender : function(ct, position) 
33371     {
33372         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33373         
33374         if(this.bullets.length){
33375             Roo.each(this.bullets, function(b){
33376                this.addItem(b);
33377             }, this);
33378         }
33379         
33380         this.format();
33381         
33382     },
33383     
33384     addItem : function(cfg)
33385     {
33386         var item = new Roo.bootstrap.NavProgressItem(cfg);
33387         
33388         item.parentId = this.id;
33389         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33390         
33391         if(cfg.html){
33392             var top = new Roo.bootstrap.Element({
33393                 tag : 'div',
33394                 cls : 'roo-navigation-bar-text'
33395             });
33396             
33397             var bottom = new Roo.bootstrap.Element({
33398                 tag : 'div',
33399                 cls : 'roo-navigation-bar-text'
33400             });
33401             
33402             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33403             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33404             
33405             var topText = new Roo.bootstrap.Element({
33406                 tag : 'span',
33407                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33408             });
33409             
33410             var bottomText = new Roo.bootstrap.Element({
33411                 tag : 'span',
33412                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33413             });
33414             
33415             topText.onRender(top.el, null);
33416             bottomText.onRender(bottom.el, null);
33417             
33418             item.topEl = top;
33419             item.bottomEl = bottom;
33420         }
33421         
33422         this.barItems.push(item);
33423         
33424         return item;
33425     },
33426     
33427     getActive : function()
33428     {
33429         var active = false;
33430         
33431         Roo.each(this.barItems, function(v){
33432             
33433             if (!v.isActive()) {
33434                 return;
33435             }
33436             
33437             active = v;
33438             return false;
33439             
33440         });
33441         
33442         return active;
33443     },
33444     
33445     setActiveItem : function(item)
33446     {
33447         var prev = false;
33448         
33449         Roo.each(this.barItems, function(v){
33450             if (v.rid == item.rid) {
33451                 return ;
33452             }
33453             
33454             if (v.isActive()) {
33455                 v.setActive(false);
33456                 prev = v;
33457             }
33458         });
33459
33460         item.setActive(true);
33461         
33462         this.fireEvent('changed', this, item, prev);
33463     },
33464     
33465     getBarItem: function(rid)
33466     {
33467         var ret = false;
33468         
33469         Roo.each(this.barItems, function(e) {
33470             if (e.rid != rid) {
33471                 return;
33472             }
33473             
33474             ret =  e;
33475             return false;
33476         });
33477         
33478         return ret;
33479     },
33480     
33481     indexOfItem : function(item)
33482     {
33483         var index = false;
33484         
33485         Roo.each(this.barItems, function(v, i){
33486             
33487             if (v.rid != item.rid) {
33488                 return;
33489             }
33490             
33491             index = i;
33492             return false
33493         });
33494         
33495         return index;
33496     },
33497     
33498     setActiveNext : function()
33499     {
33500         var i = this.indexOfItem(this.getActive());
33501         
33502         if (i > this.barItems.length) {
33503             return;
33504         }
33505         
33506         this.setActiveItem(this.barItems[i+1]);
33507     },
33508     
33509     setActivePrev : function()
33510     {
33511         var i = this.indexOfItem(this.getActive());
33512         
33513         if (i  < 1) {
33514             return;
33515         }
33516         
33517         this.setActiveItem(this.barItems[i-1]);
33518     },
33519     
33520     format : function()
33521     {
33522         if(!this.barItems.length){
33523             return;
33524         }
33525      
33526         var width = 100 / this.barItems.length;
33527         
33528         Roo.each(this.barItems, function(i){
33529             i.el.setStyle('width', width + '%');
33530             i.topEl.el.setStyle('width', width + '%');
33531             i.bottomEl.el.setStyle('width', width + '%');
33532         }, this);
33533         
33534     }
33535     
33536 });
33537 /*
33538  * - LGPL
33539  *
33540  * Nav Progress Item
33541  * 
33542  */
33543
33544 /**
33545  * @class Roo.bootstrap.NavProgressItem
33546  * @extends Roo.bootstrap.Component
33547  * Bootstrap NavProgressItem class
33548  * @cfg {String} rid the reference id
33549  * @cfg {Boolean} active (true|false) Is item active default false
33550  * @cfg {Boolean} disabled (true|false) Is item active default false
33551  * @cfg {String} html
33552  * @cfg {String} position (top|bottom) text position default bottom
33553  * @cfg {String} icon show icon instead of number
33554  * 
33555  * @constructor
33556  * Create a new NavProgressItem
33557  * @param {Object} config The config object
33558  */
33559 Roo.bootstrap.NavProgressItem = function(config){
33560     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33561     this.addEvents({
33562         // raw events
33563         /**
33564          * @event click
33565          * The raw click event for the entire grid.
33566          * @param {Roo.bootstrap.NavProgressItem} this
33567          * @param {Roo.EventObject} e
33568          */
33569         "click" : true
33570     });
33571    
33572 };
33573
33574 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33575     
33576     rid : '',
33577     active : false,
33578     disabled : false,
33579     html : '',
33580     position : 'bottom',
33581     icon : false,
33582     
33583     getAutoCreate : function()
33584     {
33585         var iconCls = 'roo-navigation-bar-item-icon';
33586         
33587         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33588         
33589         var cfg = {
33590             tag: 'li',
33591             cls: 'roo-navigation-bar-item',
33592             cn : [
33593                 {
33594                     tag : 'i',
33595                     cls : iconCls
33596                 }
33597             ]
33598         };
33599         
33600         if(this.active){
33601             cfg.cls += ' active';
33602         }
33603         if(this.disabled){
33604             cfg.cls += ' disabled';
33605         }
33606         
33607         return cfg;
33608     },
33609     
33610     disable : function()
33611     {
33612         this.setDisabled(true);
33613     },
33614     
33615     enable : function()
33616     {
33617         this.setDisabled(false);
33618     },
33619     
33620     initEvents: function() 
33621     {
33622         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33623         
33624         this.iconEl.on('click', this.onClick, this);
33625     },
33626     
33627     onClick : function(e)
33628     {
33629         e.preventDefault();
33630         
33631         if(this.disabled){
33632             return;
33633         }
33634         
33635         if(this.fireEvent('click', this, e) === false){
33636             return;
33637         };
33638         
33639         this.parent().setActiveItem(this);
33640     },
33641     
33642     isActive: function () 
33643     {
33644         return this.active;
33645     },
33646     
33647     setActive : function(state)
33648     {
33649         if(this.active == state){
33650             return;
33651         }
33652         
33653         this.active = state;
33654         
33655         if (state) {
33656             this.el.addClass('active');
33657             return;
33658         }
33659         
33660         this.el.removeClass('active');
33661         
33662         return;
33663     },
33664     
33665     setDisabled : function(state)
33666     {
33667         if(this.disabled == state){
33668             return;
33669         }
33670         
33671         this.disabled = state;
33672         
33673         if (state) {
33674             this.el.addClass('disabled');
33675             return;
33676         }
33677         
33678         this.el.removeClass('disabled');
33679     },
33680     
33681     tooltipEl : function()
33682     {
33683         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33684     }
33685 });
33686  
33687
33688  /*
33689  * - LGPL
33690  *
33691  * FieldLabel
33692  * 
33693  */
33694
33695 /**
33696  * @class Roo.bootstrap.FieldLabel
33697  * @extends Roo.bootstrap.Component
33698  * Bootstrap FieldLabel class
33699  * @cfg {String} html contents of the element
33700  * @cfg {String} tag tag of the element default label
33701  * @cfg {String} cls class of the element
33702  * @cfg {String} target label target 
33703  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33704  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33705  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33706  * @cfg {String} iconTooltip default "This field is required"
33707  * @cfg {String} indicatorpos (left|right) default left
33708  * 
33709  * @constructor
33710  * Create a new FieldLabel
33711  * @param {Object} config The config object
33712  */
33713
33714 Roo.bootstrap.FieldLabel = function(config){
33715     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33716     
33717     this.addEvents({
33718             /**
33719              * @event invalid
33720              * Fires after the field has been marked as invalid.
33721              * @param {Roo.form.FieldLabel} this
33722              * @param {String} msg The validation message
33723              */
33724             invalid : true,
33725             /**
33726              * @event valid
33727              * Fires after the field has been validated with no errors.
33728              * @param {Roo.form.FieldLabel} this
33729              */
33730             valid : true
33731         });
33732 };
33733
33734 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33735     
33736     tag: 'label',
33737     cls: '',
33738     html: '',
33739     target: '',
33740     allowBlank : true,
33741     invalidClass : 'has-warning',
33742     validClass : 'has-success',
33743     iconTooltip : 'This field is required',
33744     indicatorpos : 'left',
33745     
33746     getAutoCreate : function(){
33747         
33748         var cls = "";
33749         if (!this.allowBlank) {
33750             cls  = "visible";
33751         }
33752         
33753         var cfg = {
33754             tag : this.tag,
33755             cls : 'roo-bootstrap-field-label ' + this.cls,
33756             for : this.target,
33757             cn : [
33758                 {
33759                     tag : 'i',
33760                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33761                     tooltip : this.iconTooltip
33762                 },
33763                 {
33764                     tag : 'span',
33765                     html : this.html
33766                 }
33767             ] 
33768         };
33769         
33770         if(this.indicatorpos == 'right'){
33771             var cfg = {
33772                 tag : this.tag,
33773                 cls : 'roo-bootstrap-field-label ' + this.cls,
33774                 for : this.target,
33775                 cn : [
33776                     {
33777                         tag : 'span',
33778                         html : this.html
33779                     },
33780                     {
33781                         tag : 'i',
33782                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33783                         tooltip : this.iconTooltip
33784                     }
33785                 ] 
33786             };
33787         }
33788         
33789         return cfg;
33790     },
33791     
33792     initEvents: function() 
33793     {
33794         Roo.bootstrap.Element.superclass.initEvents.call(this);
33795         
33796         this.indicator = this.indicatorEl();
33797         
33798         if(this.indicator){
33799             this.indicator.removeClass('visible');
33800             this.indicator.addClass('invisible');
33801         }
33802         
33803         Roo.bootstrap.FieldLabel.register(this);
33804     },
33805     
33806     indicatorEl : function()
33807     {
33808         var indicator = this.el.select('i.roo-required-indicator',true).first();
33809         
33810         if(!indicator){
33811             return false;
33812         }
33813         
33814         return indicator;
33815         
33816     },
33817     
33818     /**
33819      * Mark this field as valid
33820      */
33821     markValid : function()
33822     {
33823         if(this.indicator){
33824             this.indicator.removeClass('visible');
33825             this.indicator.addClass('invisible');
33826         }
33827         if (Roo.bootstrap.version == 3) {
33828             this.el.removeClass(this.invalidClass);
33829             this.el.addClass(this.validClass);
33830         } else {
33831             this.el.removeClass('is-invalid');
33832             this.el.addClass('is-valid');
33833         }
33834         
33835         
33836         this.fireEvent('valid', this);
33837     },
33838     
33839     /**
33840      * Mark this field as invalid
33841      * @param {String} msg The validation message
33842      */
33843     markInvalid : function(msg)
33844     {
33845         if(this.indicator){
33846             this.indicator.removeClass('invisible');
33847             this.indicator.addClass('visible');
33848         }
33849           if (Roo.bootstrap.version == 3) {
33850             this.el.removeClass(this.validClass);
33851             this.el.addClass(this.invalidClass);
33852         } else {
33853             this.el.removeClass('is-valid');
33854             this.el.addClass('is-invalid');
33855         }
33856         
33857         
33858         this.fireEvent('invalid', this, msg);
33859     }
33860     
33861    
33862 });
33863
33864 Roo.apply(Roo.bootstrap.FieldLabel, {
33865     
33866     groups: {},
33867     
33868      /**
33869     * register a FieldLabel Group
33870     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33871     */
33872     register : function(label)
33873     {
33874         if(this.groups.hasOwnProperty(label.target)){
33875             return;
33876         }
33877      
33878         this.groups[label.target] = label;
33879         
33880     },
33881     /**
33882     * fetch a FieldLabel Group based on the target
33883     * @param {string} target
33884     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33885     */
33886     get: function(target) {
33887         if (typeof(this.groups[target]) == 'undefined') {
33888             return false;
33889         }
33890         
33891         return this.groups[target] ;
33892     }
33893 });
33894
33895  
33896
33897  /*
33898  * - LGPL
33899  *
33900  * page DateSplitField.
33901  * 
33902  */
33903
33904
33905 /**
33906  * @class Roo.bootstrap.DateSplitField
33907  * @extends Roo.bootstrap.Component
33908  * Bootstrap DateSplitField class
33909  * @cfg {string} fieldLabel - the label associated
33910  * @cfg {Number} labelWidth set the width of label (0-12)
33911  * @cfg {String} labelAlign (top|left)
33912  * @cfg {Boolean} dayAllowBlank (true|false) default false
33913  * @cfg {Boolean} monthAllowBlank (true|false) default false
33914  * @cfg {Boolean} yearAllowBlank (true|false) default false
33915  * @cfg {string} dayPlaceholder 
33916  * @cfg {string} monthPlaceholder
33917  * @cfg {string} yearPlaceholder
33918  * @cfg {string} dayFormat default 'd'
33919  * @cfg {string} monthFormat default 'm'
33920  * @cfg {string} yearFormat default 'Y'
33921  * @cfg {Number} labellg set the width of label (1-12)
33922  * @cfg {Number} labelmd set the width of label (1-12)
33923  * @cfg {Number} labelsm set the width of label (1-12)
33924  * @cfg {Number} labelxs set the width of label (1-12)
33925
33926  *     
33927  * @constructor
33928  * Create a new DateSplitField
33929  * @param {Object} config The config object
33930  */
33931
33932 Roo.bootstrap.DateSplitField = function(config){
33933     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33934     
33935     this.addEvents({
33936         // raw events
33937          /**
33938          * @event years
33939          * getting the data of years
33940          * @param {Roo.bootstrap.DateSplitField} this
33941          * @param {Object} years
33942          */
33943         "years" : true,
33944         /**
33945          * @event days
33946          * getting the data of days
33947          * @param {Roo.bootstrap.DateSplitField} this
33948          * @param {Object} days
33949          */
33950         "days" : true,
33951         /**
33952          * @event invalid
33953          * Fires after the field has been marked as invalid.
33954          * @param {Roo.form.Field} this
33955          * @param {String} msg The validation message
33956          */
33957         invalid : true,
33958        /**
33959          * @event valid
33960          * Fires after the field has been validated with no errors.
33961          * @param {Roo.form.Field} this
33962          */
33963         valid : true
33964     });
33965 };
33966
33967 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33968     
33969     fieldLabel : '',
33970     labelAlign : 'top',
33971     labelWidth : 3,
33972     dayAllowBlank : false,
33973     monthAllowBlank : false,
33974     yearAllowBlank : false,
33975     dayPlaceholder : '',
33976     monthPlaceholder : '',
33977     yearPlaceholder : '',
33978     dayFormat : 'd',
33979     monthFormat : 'm',
33980     yearFormat : 'Y',
33981     isFormField : true,
33982     labellg : 0,
33983     labelmd : 0,
33984     labelsm : 0,
33985     labelxs : 0,
33986     
33987     getAutoCreate : function()
33988     {
33989         var cfg = {
33990             tag : 'div',
33991             cls : 'row roo-date-split-field-group',
33992             cn : [
33993                 {
33994                     tag : 'input',
33995                     type : 'hidden',
33996                     cls : 'form-hidden-field roo-date-split-field-group-value',
33997                     name : this.name
33998                 }
33999             ]
34000         };
34001         
34002         var labelCls = 'col-md-12';
34003         var contentCls = 'col-md-4';
34004         
34005         if(this.fieldLabel){
34006             
34007             var label = {
34008                 tag : 'div',
34009                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34010                 cn : [
34011                     {
34012                         tag : 'label',
34013                         html : this.fieldLabel
34014                     }
34015                 ]
34016             };
34017             
34018             if(this.labelAlign == 'left'){
34019             
34020                 if(this.labelWidth > 12){
34021                     label.style = "width: " + this.labelWidth + 'px';
34022                 }
34023
34024                 if(this.labelWidth < 13 && this.labelmd == 0){
34025                     this.labelmd = this.labelWidth;
34026                 }
34027
34028                 if(this.labellg > 0){
34029                     labelCls = ' col-lg-' + this.labellg;
34030                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34031                 }
34032
34033                 if(this.labelmd > 0){
34034                     labelCls = ' col-md-' + this.labelmd;
34035                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34036                 }
34037
34038                 if(this.labelsm > 0){
34039                     labelCls = ' col-sm-' + this.labelsm;
34040                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34041                 }
34042
34043                 if(this.labelxs > 0){
34044                     labelCls = ' col-xs-' + this.labelxs;
34045                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34046                 }
34047             }
34048             
34049             label.cls += ' ' + labelCls;
34050             
34051             cfg.cn.push(label);
34052         }
34053         
34054         Roo.each(['day', 'month', 'year'], function(t){
34055             cfg.cn.push({
34056                 tag : 'div',
34057                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34058             });
34059         }, this);
34060         
34061         return cfg;
34062     },
34063     
34064     inputEl: function ()
34065     {
34066         return this.el.select('.roo-date-split-field-group-value', true).first();
34067     },
34068     
34069     onRender : function(ct, position) 
34070     {
34071         var _this = this;
34072         
34073         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34074         
34075         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34076         
34077         this.dayField = new Roo.bootstrap.ComboBox({
34078             allowBlank : this.dayAllowBlank,
34079             alwaysQuery : true,
34080             displayField : 'value',
34081             editable : false,
34082             fieldLabel : '',
34083             forceSelection : true,
34084             mode : 'local',
34085             placeholder : this.dayPlaceholder,
34086             selectOnFocus : true,
34087             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34088             triggerAction : 'all',
34089             typeAhead : true,
34090             valueField : 'value',
34091             store : new Roo.data.SimpleStore({
34092                 data : (function() {    
34093                     var days = [];
34094                     _this.fireEvent('days', _this, days);
34095                     return days;
34096                 })(),
34097                 fields : [ 'value' ]
34098             }),
34099             listeners : {
34100                 select : function (_self, record, index)
34101                 {
34102                     _this.setValue(_this.getValue());
34103                 }
34104             }
34105         });
34106
34107         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34108         
34109         this.monthField = new Roo.bootstrap.MonthField({
34110             after : '<i class=\"fa fa-calendar\"></i>',
34111             allowBlank : this.monthAllowBlank,
34112             placeholder : this.monthPlaceholder,
34113             readOnly : true,
34114             listeners : {
34115                 render : function (_self)
34116                 {
34117                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34118                         e.preventDefault();
34119                         _self.focus();
34120                     });
34121                 },
34122                 select : function (_self, oldvalue, newvalue)
34123                 {
34124                     _this.setValue(_this.getValue());
34125                 }
34126             }
34127         });
34128         
34129         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34130         
34131         this.yearField = new Roo.bootstrap.ComboBox({
34132             allowBlank : this.yearAllowBlank,
34133             alwaysQuery : true,
34134             displayField : 'value',
34135             editable : false,
34136             fieldLabel : '',
34137             forceSelection : true,
34138             mode : 'local',
34139             placeholder : this.yearPlaceholder,
34140             selectOnFocus : true,
34141             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34142             triggerAction : 'all',
34143             typeAhead : true,
34144             valueField : 'value',
34145             store : new Roo.data.SimpleStore({
34146                 data : (function() {
34147                     var years = [];
34148                     _this.fireEvent('years', _this, years);
34149                     return years;
34150                 })(),
34151                 fields : [ 'value' ]
34152             }),
34153             listeners : {
34154                 select : function (_self, record, index)
34155                 {
34156                     _this.setValue(_this.getValue());
34157                 }
34158             }
34159         });
34160
34161         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34162     },
34163     
34164     setValue : function(v, format)
34165     {
34166         this.inputEl.dom.value = v;
34167         
34168         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34169         
34170         var d = Date.parseDate(v, f);
34171         
34172         if(!d){
34173             this.validate();
34174             return;
34175         }
34176         
34177         this.setDay(d.format(this.dayFormat));
34178         this.setMonth(d.format(this.monthFormat));
34179         this.setYear(d.format(this.yearFormat));
34180         
34181         this.validate();
34182         
34183         return;
34184     },
34185     
34186     setDay : function(v)
34187     {
34188         this.dayField.setValue(v);
34189         this.inputEl.dom.value = this.getValue();
34190         this.validate();
34191         return;
34192     },
34193     
34194     setMonth : function(v)
34195     {
34196         this.monthField.setValue(v, true);
34197         this.inputEl.dom.value = this.getValue();
34198         this.validate();
34199         return;
34200     },
34201     
34202     setYear : function(v)
34203     {
34204         this.yearField.setValue(v);
34205         this.inputEl.dom.value = this.getValue();
34206         this.validate();
34207         return;
34208     },
34209     
34210     getDay : function()
34211     {
34212         return this.dayField.getValue();
34213     },
34214     
34215     getMonth : function()
34216     {
34217         return this.monthField.getValue();
34218     },
34219     
34220     getYear : function()
34221     {
34222         return this.yearField.getValue();
34223     },
34224     
34225     getValue : function()
34226     {
34227         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34228         
34229         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34230         
34231         return date;
34232     },
34233     
34234     reset : function()
34235     {
34236         this.setDay('');
34237         this.setMonth('');
34238         this.setYear('');
34239         this.inputEl.dom.value = '';
34240         this.validate();
34241         return;
34242     },
34243     
34244     validate : function()
34245     {
34246         var d = this.dayField.validate();
34247         var m = this.monthField.validate();
34248         var y = this.yearField.validate();
34249         
34250         var valid = true;
34251         
34252         if(
34253                 (!this.dayAllowBlank && !d) ||
34254                 (!this.monthAllowBlank && !m) ||
34255                 (!this.yearAllowBlank && !y)
34256         ){
34257             valid = false;
34258         }
34259         
34260         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34261             return valid;
34262         }
34263         
34264         if(valid){
34265             this.markValid();
34266             return valid;
34267         }
34268         
34269         this.markInvalid();
34270         
34271         return valid;
34272     },
34273     
34274     markValid : function()
34275     {
34276         
34277         var label = this.el.select('label', true).first();
34278         var icon = this.el.select('i.fa-star', true).first();
34279
34280         if(label && icon){
34281             icon.remove();
34282         }
34283         
34284         this.fireEvent('valid', this);
34285     },
34286     
34287      /**
34288      * Mark this field as invalid
34289      * @param {String} msg The validation message
34290      */
34291     markInvalid : function(msg)
34292     {
34293         
34294         var label = this.el.select('label', true).first();
34295         var icon = this.el.select('i.fa-star', true).first();
34296
34297         if(label && !icon){
34298             this.el.select('.roo-date-split-field-label', true).createChild({
34299                 tag : 'i',
34300                 cls : 'text-danger fa fa-lg fa-star',
34301                 tooltip : 'This field is required',
34302                 style : 'margin-right:5px;'
34303             }, label, true);
34304         }
34305         
34306         this.fireEvent('invalid', this, msg);
34307     },
34308     
34309     clearInvalid : function()
34310     {
34311         var label = this.el.select('label', true).first();
34312         var icon = this.el.select('i.fa-star', true).first();
34313
34314         if(label && icon){
34315             icon.remove();
34316         }
34317         
34318         this.fireEvent('valid', this);
34319     },
34320     
34321     getName: function()
34322     {
34323         return this.name;
34324     }
34325     
34326 });
34327
34328  /**
34329  *
34330  * This is based on 
34331  * http://masonry.desandro.com
34332  *
34333  * The idea is to render all the bricks based on vertical width...
34334  *
34335  * The original code extends 'outlayer' - we might need to use that....
34336  * 
34337  */
34338
34339
34340 /**
34341  * @class Roo.bootstrap.LayoutMasonry
34342  * @extends Roo.bootstrap.Component
34343  * Bootstrap Layout Masonry class
34344  * 
34345  * @constructor
34346  * Create a new Element
34347  * @param {Object} config The config object
34348  */
34349
34350 Roo.bootstrap.LayoutMasonry = function(config){
34351     
34352     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34353     
34354     this.bricks = [];
34355     
34356     Roo.bootstrap.LayoutMasonry.register(this);
34357     
34358     this.addEvents({
34359         // raw events
34360         /**
34361          * @event layout
34362          * Fire after layout the items
34363          * @param {Roo.bootstrap.LayoutMasonry} this
34364          * @param {Roo.EventObject} e
34365          */
34366         "layout" : true
34367     });
34368     
34369 };
34370
34371 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34372     
34373     /**
34374      * @cfg {Boolean} isLayoutInstant = no animation?
34375      */   
34376     isLayoutInstant : false, // needed?
34377    
34378     /**
34379      * @cfg {Number} boxWidth  width of the columns
34380      */   
34381     boxWidth : 450,
34382     
34383       /**
34384      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34385      */   
34386     boxHeight : 0,
34387     
34388     /**
34389      * @cfg {Number} padWidth padding below box..
34390      */   
34391     padWidth : 10, 
34392     
34393     /**
34394      * @cfg {Number} gutter gutter width..
34395      */   
34396     gutter : 10,
34397     
34398      /**
34399      * @cfg {Number} maxCols maximum number of columns
34400      */   
34401     
34402     maxCols: 0,
34403     
34404     /**
34405      * @cfg {Boolean} isAutoInitial defalut true
34406      */   
34407     isAutoInitial : true, 
34408     
34409     containerWidth: 0,
34410     
34411     /**
34412      * @cfg {Boolean} isHorizontal defalut false
34413      */   
34414     isHorizontal : false, 
34415
34416     currentSize : null,
34417     
34418     tag: 'div',
34419     
34420     cls: '',
34421     
34422     bricks: null, //CompositeElement
34423     
34424     cols : 1,
34425     
34426     _isLayoutInited : false,
34427     
34428 //    isAlternative : false, // only use for vertical layout...
34429     
34430     /**
34431      * @cfg {Number} alternativePadWidth padding below box..
34432      */   
34433     alternativePadWidth : 50,
34434     
34435     selectedBrick : [],
34436     
34437     getAutoCreate : function(){
34438         
34439         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34440         
34441         var cfg = {
34442             tag: this.tag,
34443             cls: 'blog-masonary-wrapper ' + this.cls,
34444             cn : {
34445                 cls : 'mas-boxes masonary'
34446             }
34447         };
34448         
34449         return cfg;
34450     },
34451     
34452     getChildContainer: function( )
34453     {
34454         if (this.boxesEl) {
34455             return this.boxesEl;
34456         }
34457         
34458         this.boxesEl = this.el.select('.mas-boxes').first();
34459         
34460         return this.boxesEl;
34461     },
34462     
34463     
34464     initEvents : function()
34465     {
34466         var _this = this;
34467         
34468         if(this.isAutoInitial){
34469             Roo.log('hook children rendered');
34470             this.on('childrenrendered', function() {
34471                 Roo.log('children rendered');
34472                 _this.initial();
34473             } ,this);
34474         }
34475     },
34476     
34477     initial : function()
34478     {
34479         this.selectedBrick = [];
34480         
34481         this.currentSize = this.el.getBox(true);
34482         
34483         Roo.EventManager.onWindowResize(this.resize, this); 
34484
34485         if(!this.isAutoInitial){
34486             this.layout();
34487             return;
34488         }
34489         
34490         this.layout();
34491         
34492         return;
34493         //this.layout.defer(500,this);
34494         
34495     },
34496     
34497     resize : function()
34498     {
34499         var cs = this.el.getBox(true);
34500         
34501         if (
34502                 this.currentSize.width == cs.width && 
34503                 this.currentSize.x == cs.x && 
34504                 this.currentSize.height == cs.height && 
34505                 this.currentSize.y == cs.y 
34506         ) {
34507             Roo.log("no change in with or X or Y");
34508             return;
34509         }
34510         
34511         this.currentSize = cs;
34512         
34513         this.layout();
34514         
34515     },
34516     
34517     layout : function()
34518     {   
34519         this._resetLayout();
34520         
34521         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34522         
34523         this.layoutItems( isInstant );
34524       
34525         this._isLayoutInited = true;
34526         
34527         this.fireEvent('layout', this);
34528         
34529     },
34530     
34531     _resetLayout : function()
34532     {
34533         if(this.isHorizontal){
34534             this.horizontalMeasureColumns();
34535             return;
34536         }
34537         
34538         this.verticalMeasureColumns();
34539         
34540     },
34541     
34542     verticalMeasureColumns : function()
34543     {
34544         this.getContainerWidth();
34545         
34546 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34547 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34548 //            return;
34549 //        }
34550         
34551         var boxWidth = this.boxWidth + this.padWidth;
34552         
34553         if(this.containerWidth < this.boxWidth){
34554             boxWidth = this.containerWidth
34555         }
34556         
34557         var containerWidth = this.containerWidth;
34558         
34559         var cols = Math.floor(containerWidth / boxWidth);
34560         
34561         this.cols = Math.max( cols, 1 );
34562         
34563         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34564         
34565         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34566         
34567         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34568         
34569         this.colWidth = boxWidth + avail - this.padWidth;
34570         
34571         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34572         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34573     },
34574     
34575     horizontalMeasureColumns : function()
34576     {
34577         this.getContainerWidth();
34578         
34579         var boxWidth = this.boxWidth;
34580         
34581         if(this.containerWidth < boxWidth){
34582             boxWidth = this.containerWidth;
34583         }
34584         
34585         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34586         
34587         this.el.setHeight(boxWidth);
34588         
34589     },
34590     
34591     getContainerWidth : function()
34592     {
34593         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34594     },
34595     
34596     layoutItems : function( isInstant )
34597     {
34598         Roo.log(this.bricks);
34599         
34600         var items = Roo.apply([], this.bricks);
34601         
34602         if(this.isHorizontal){
34603             this._horizontalLayoutItems( items , isInstant );
34604             return;
34605         }
34606         
34607 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34608 //            this._verticalAlternativeLayoutItems( items , isInstant );
34609 //            return;
34610 //        }
34611         
34612         this._verticalLayoutItems( items , isInstant );
34613         
34614     },
34615     
34616     _verticalLayoutItems : function ( items , isInstant)
34617     {
34618         if ( !items || !items.length ) {
34619             return;
34620         }
34621         
34622         var standard = [
34623             ['xs', 'xs', 'xs', 'tall'],
34624             ['xs', 'xs', 'tall'],
34625             ['xs', 'xs', 'sm'],
34626             ['xs', 'xs', 'xs'],
34627             ['xs', 'tall'],
34628             ['xs', 'sm'],
34629             ['xs', 'xs'],
34630             ['xs'],
34631             
34632             ['sm', 'xs', 'xs'],
34633             ['sm', 'xs'],
34634             ['sm'],
34635             
34636             ['tall', 'xs', 'xs', 'xs'],
34637             ['tall', 'xs', 'xs'],
34638             ['tall', 'xs'],
34639             ['tall']
34640             
34641         ];
34642         
34643         var queue = [];
34644         
34645         var boxes = [];
34646         
34647         var box = [];
34648         
34649         Roo.each(items, function(item, k){
34650             
34651             switch (item.size) {
34652                 // these layouts take up a full box,
34653                 case 'md' :
34654                 case 'md-left' :
34655                 case 'md-right' :
34656                 case 'wide' :
34657                     
34658                     if(box.length){
34659                         boxes.push(box);
34660                         box = [];
34661                     }
34662                     
34663                     boxes.push([item]);
34664                     
34665                     break;
34666                     
34667                 case 'xs' :
34668                 case 'sm' :
34669                 case 'tall' :
34670                     
34671                     box.push(item);
34672                     
34673                     break;
34674                 default :
34675                     break;
34676                     
34677             }
34678             
34679         }, this);
34680         
34681         if(box.length){
34682             boxes.push(box);
34683             box = [];
34684         }
34685         
34686         var filterPattern = function(box, length)
34687         {
34688             if(!box.length){
34689                 return;
34690             }
34691             
34692             var match = false;
34693             
34694             var pattern = box.slice(0, length);
34695             
34696             var format = [];
34697             
34698             Roo.each(pattern, function(i){
34699                 format.push(i.size);
34700             }, this);
34701             
34702             Roo.each(standard, function(s){
34703                 
34704                 if(String(s) != String(format)){
34705                     return;
34706                 }
34707                 
34708                 match = true;
34709                 return false;
34710                 
34711             }, this);
34712             
34713             if(!match && length == 1){
34714                 return;
34715             }
34716             
34717             if(!match){
34718                 filterPattern(box, length - 1);
34719                 return;
34720             }
34721                 
34722             queue.push(pattern);
34723
34724             box = box.slice(length, box.length);
34725
34726             filterPattern(box, 4);
34727
34728             return;
34729             
34730         }
34731         
34732         Roo.each(boxes, function(box, k){
34733             
34734             if(!box.length){
34735                 return;
34736             }
34737             
34738             if(box.length == 1){
34739                 queue.push(box);
34740                 return;
34741             }
34742             
34743             filterPattern(box, 4);
34744             
34745         }, this);
34746         
34747         this._processVerticalLayoutQueue( queue, isInstant );
34748         
34749     },
34750     
34751 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34752 //    {
34753 //        if ( !items || !items.length ) {
34754 //            return;
34755 //        }
34756 //
34757 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34758 //        
34759 //    },
34760     
34761     _horizontalLayoutItems : function ( items , isInstant)
34762     {
34763         if ( !items || !items.length || items.length < 3) {
34764             return;
34765         }
34766         
34767         items.reverse();
34768         
34769         var eItems = items.slice(0, 3);
34770         
34771         items = items.slice(3, items.length);
34772         
34773         var standard = [
34774             ['xs', 'xs', 'xs', 'wide'],
34775             ['xs', 'xs', 'wide'],
34776             ['xs', 'xs', 'sm'],
34777             ['xs', 'xs', 'xs'],
34778             ['xs', 'wide'],
34779             ['xs', 'sm'],
34780             ['xs', 'xs'],
34781             ['xs'],
34782             
34783             ['sm', 'xs', 'xs'],
34784             ['sm', 'xs'],
34785             ['sm'],
34786             
34787             ['wide', 'xs', 'xs', 'xs'],
34788             ['wide', 'xs', 'xs'],
34789             ['wide', 'xs'],
34790             ['wide'],
34791             
34792             ['wide-thin']
34793         ];
34794         
34795         var queue = [];
34796         
34797         var boxes = [];
34798         
34799         var box = [];
34800         
34801         Roo.each(items, function(item, k){
34802             
34803             switch (item.size) {
34804                 case 'md' :
34805                 case 'md-left' :
34806                 case 'md-right' :
34807                 case 'tall' :
34808                     
34809                     if(box.length){
34810                         boxes.push(box);
34811                         box = [];
34812                     }
34813                     
34814                     boxes.push([item]);
34815                     
34816                     break;
34817                     
34818                 case 'xs' :
34819                 case 'sm' :
34820                 case 'wide' :
34821                 case 'wide-thin' :
34822                     
34823                     box.push(item);
34824                     
34825                     break;
34826                 default :
34827                     break;
34828                     
34829             }
34830             
34831         }, this);
34832         
34833         if(box.length){
34834             boxes.push(box);
34835             box = [];
34836         }
34837         
34838         var filterPattern = function(box, length)
34839         {
34840             if(!box.length){
34841                 return;
34842             }
34843             
34844             var match = false;
34845             
34846             var pattern = box.slice(0, length);
34847             
34848             var format = [];
34849             
34850             Roo.each(pattern, function(i){
34851                 format.push(i.size);
34852             }, this);
34853             
34854             Roo.each(standard, function(s){
34855                 
34856                 if(String(s) != String(format)){
34857                     return;
34858                 }
34859                 
34860                 match = true;
34861                 return false;
34862                 
34863             }, this);
34864             
34865             if(!match && length == 1){
34866                 return;
34867             }
34868             
34869             if(!match){
34870                 filterPattern(box, length - 1);
34871                 return;
34872             }
34873                 
34874             queue.push(pattern);
34875
34876             box = box.slice(length, box.length);
34877
34878             filterPattern(box, 4);
34879
34880             return;
34881             
34882         }
34883         
34884         Roo.each(boxes, function(box, k){
34885             
34886             if(!box.length){
34887                 return;
34888             }
34889             
34890             if(box.length == 1){
34891                 queue.push(box);
34892                 return;
34893             }
34894             
34895             filterPattern(box, 4);
34896             
34897         }, this);
34898         
34899         
34900         var prune = [];
34901         
34902         var pos = this.el.getBox(true);
34903         
34904         var minX = pos.x;
34905         
34906         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34907         
34908         var hit_end = false;
34909         
34910         Roo.each(queue, function(box){
34911             
34912             if(hit_end){
34913                 
34914                 Roo.each(box, function(b){
34915                 
34916                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34917                     b.el.hide();
34918
34919                 }, this);
34920
34921                 return;
34922             }
34923             
34924             var mx = 0;
34925             
34926             Roo.each(box, function(b){
34927                 
34928                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34929                 b.el.show();
34930
34931                 mx = Math.max(mx, b.x);
34932                 
34933             }, this);
34934             
34935             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34936             
34937             if(maxX < minX){
34938                 
34939                 Roo.each(box, function(b){
34940                 
34941                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34942                     b.el.hide();
34943                     
34944                 }, this);
34945                 
34946                 hit_end = true;
34947                 
34948                 return;
34949             }
34950             
34951             prune.push(box);
34952             
34953         }, this);
34954         
34955         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34956     },
34957     
34958     /** Sets position of item in DOM
34959     * @param {Element} item
34960     * @param {Number} x - horizontal position
34961     * @param {Number} y - vertical position
34962     * @param {Boolean} isInstant - disables transitions
34963     */
34964     _processVerticalLayoutQueue : function( queue, isInstant )
34965     {
34966         var pos = this.el.getBox(true);
34967         var x = pos.x;
34968         var y = pos.y;
34969         var maxY = [];
34970         
34971         for (var i = 0; i < this.cols; i++){
34972             maxY[i] = pos.y;
34973         }
34974         
34975         Roo.each(queue, function(box, k){
34976             
34977             var col = k % this.cols;
34978             
34979             Roo.each(box, function(b,kk){
34980                 
34981                 b.el.position('absolute');
34982                 
34983                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34984                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34985                 
34986                 if(b.size == 'md-left' || b.size == 'md-right'){
34987                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34988                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34989                 }
34990                 
34991                 b.el.setWidth(width);
34992                 b.el.setHeight(height);
34993                 // iframe?
34994                 b.el.select('iframe',true).setSize(width,height);
34995                 
34996             }, this);
34997             
34998             for (var i = 0; i < this.cols; i++){
34999                 
35000                 if(maxY[i] < maxY[col]){
35001                     col = i;
35002                     continue;
35003                 }
35004                 
35005                 col = Math.min(col, i);
35006                 
35007             }
35008             
35009             x = pos.x + col * (this.colWidth + this.padWidth);
35010             
35011             y = maxY[col];
35012             
35013             var positions = [];
35014             
35015             switch (box.length){
35016                 case 1 :
35017                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35018                     break;
35019                 case 2 :
35020                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35021                     break;
35022                 case 3 :
35023                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35024                     break;
35025                 case 4 :
35026                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35027                     break;
35028                 default :
35029                     break;
35030             }
35031             
35032             Roo.each(box, function(b,kk){
35033                 
35034                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35035                 
35036                 var sz = b.el.getSize();
35037                 
35038                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35039                 
35040             }, this);
35041             
35042         }, this);
35043         
35044         var mY = 0;
35045         
35046         for (var i = 0; i < this.cols; i++){
35047             mY = Math.max(mY, maxY[i]);
35048         }
35049         
35050         this.el.setHeight(mY - pos.y);
35051         
35052     },
35053     
35054 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35055 //    {
35056 //        var pos = this.el.getBox(true);
35057 //        var x = pos.x;
35058 //        var y = pos.y;
35059 //        var maxX = pos.right;
35060 //        
35061 //        var maxHeight = 0;
35062 //        
35063 //        Roo.each(items, function(item, k){
35064 //            
35065 //            var c = k % 2;
35066 //            
35067 //            item.el.position('absolute');
35068 //                
35069 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35070 //
35071 //            item.el.setWidth(width);
35072 //
35073 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35074 //
35075 //            item.el.setHeight(height);
35076 //            
35077 //            if(c == 0){
35078 //                item.el.setXY([x, y], isInstant ? false : true);
35079 //            } else {
35080 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35081 //            }
35082 //            
35083 //            y = y + height + this.alternativePadWidth;
35084 //            
35085 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35086 //            
35087 //        }, this);
35088 //        
35089 //        this.el.setHeight(maxHeight);
35090 //        
35091 //    },
35092     
35093     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35094     {
35095         var pos = this.el.getBox(true);
35096         
35097         var minX = pos.x;
35098         var minY = pos.y;
35099         
35100         var maxX = pos.right;
35101         
35102         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35103         
35104         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35105         
35106         Roo.each(queue, function(box, k){
35107             
35108             Roo.each(box, function(b, kk){
35109                 
35110                 b.el.position('absolute');
35111                 
35112                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35113                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35114                 
35115                 if(b.size == 'md-left' || b.size == 'md-right'){
35116                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35117                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35118                 }
35119                 
35120                 b.el.setWidth(width);
35121                 b.el.setHeight(height);
35122                 
35123             }, this);
35124             
35125             if(!box.length){
35126                 return;
35127             }
35128             
35129             var positions = [];
35130             
35131             switch (box.length){
35132                 case 1 :
35133                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35134                     break;
35135                 case 2 :
35136                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35137                     break;
35138                 case 3 :
35139                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35140                     break;
35141                 case 4 :
35142                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35143                     break;
35144                 default :
35145                     break;
35146             }
35147             
35148             Roo.each(box, function(b,kk){
35149                 
35150                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35151                 
35152                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35153                 
35154             }, this);
35155             
35156         }, this);
35157         
35158     },
35159     
35160     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35161     {
35162         Roo.each(eItems, function(b,k){
35163             
35164             b.size = (k == 0) ? 'sm' : 'xs';
35165             b.x = (k == 0) ? 2 : 1;
35166             b.y = (k == 0) ? 2 : 1;
35167             
35168             b.el.position('absolute');
35169             
35170             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35171                 
35172             b.el.setWidth(width);
35173             
35174             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35175             
35176             b.el.setHeight(height);
35177             
35178         }, this);
35179
35180         var positions = [];
35181         
35182         positions.push({
35183             x : maxX - this.unitWidth * 2 - this.gutter,
35184             y : minY
35185         });
35186         
35187         positions.push({
35188             x : maxX - this.unitWidth,
35189             y : minY + (this.unitWidth + this.gutter) * 2
35190         });
35191         
35192         positions.push({
35193             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35194             y : minY
35195         });
35196         
35197         Roo.each(eItems, function(b,k){
35198             
35199             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35200
35201         }, this);
35202         
35203     },
35204     
35205     getVerticalOneBoxColPositions : function(x, y, box)
35206     {
35207         var pos = [];
35208         
35209         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35210         
35211         if(box[0].size == 'md-left'){
35212             rand = 0;
35213         }
35214         
35215         if(box[0].size == 'md-right'){
35216             rand = 1;
35217         }
35218         
35219         pos.push({
35220             x : x + (this.unitWidth + this.gutter) * rand,
35221             y : y
35222         });
35223         
35224         return pos;
35225     },
35226     
35227     getVerticalTwoBoxColPositions : function(x, y, box)
35228     {
35229         var pos = [];
35230         
35231         if(box[0].size == 'xs'){
35232             
35233             pos.push({
35234                 x : x,
35235                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35236             });
35237
35238             pos.push({
35239                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35240                 y : y
35241             });
35242             
35243             return pos;
35244             
35245         }
35246         
35247         pos.push({
35248             x : x,
35249             y : y
35250         });
35251
35252         pos.push({
35253             x : x + (this.unitWidth + this.gutter) * 2,
35254             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35255         });
35256         
35257         return pos;
35258         
35259     },
35260     
35261     getVerticalThreeBoxColPositions : function(x, y, box)
35262     {
35263         var pos = [];
35264         
35265         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35266             
35267             pos.push({
35268                 x : x,
35269                 y : y
35270             });
35271
35272             pos.push({
35273                 x : x + (this.unitWidth + this.gutter) * 1,
35274                 y : y
35275             });
35276             
35277             pos.push({
35278                 x : x + (this.unitWidth + this.gutter) * 2,
35279                 y : y
35280             });
35281             
35282             return pos;
35283             
35284         }
35285         
35286         if(box[0].size == 'xs' && box[1].size == 'xs'){
35287             
35288             pos.push({
35289                 x : x,
35290                 y : y
35291             });
35292
35293             pos.push({
35294                 x : x,
35295                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35296             });
35297             
35298             pos.push({
35299                 x : x + (this.unitWidth + this.gutter) * 1,
35300                 y : y
35301             });
35302             
35303             return pos;
35304             
35305         }
35306         
35307         pos.push({
35308             x : x,
35309             y : y
35310         });
35311
35312         pos.push({
35313             x : x + (this.unitWidth + this.gutter) * 2,
35314             y : y
35315         });
35316
35317         pos.push({
35318             x : x + (this.unitWidth + this.gutter) * 2,
35319             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35320         });
35321             
35322         return pos;
35323         
35324     },
35325     
35326     getVerticalFourBoxColPositions : function(x, y, box)
35327     {
35328         var pos = [];
35329         
35330         if(box[0].size == 'xs'){
35331             
35332             pos.push({
35333                 x : x,
35334                 y : y
35335             });
35336
35337             pos.push({
35338                 x : x,
35339                 y : y + (this.unitHeight + this.gutter) * 1
35340             });
35341             
35342             pos.push({
35343                 x : x,
35344                 y : y + (this.unitHeight + this.gutter) * 2
35345             });
35346             
35347             pos.push({
35348                 x : x + (this.unitWidth + this.gutter) * 1,
35349                 y : y
35350             });
35351             
35352             return pos;
35353             
35354         }
35355         
35356         pos.push({
35357             x : x,
35358             y : y
35359         });
35360
35361         pos.push({
35362             x : x + (this.unitWidth + this.gutter) * 2,
35363             y : y
35364         });
35365
35366         pos.push({
35367             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35368             y : y + (this.unitHeight + this.gutter) * 1
35369         });
35370
35371         pos.push({
35372             x : x + (this.unitWidth + this.gutter) * 2,
35373             y : y + (this.unitWidth + this.gutter) * 2
35374         });
35375
35376         return pos;
35377         
35378     },
35379     
35380     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35381     {
35382         var pos = [];
35383         
35384         if(box[0].size == 'md-left'){
35385             pos.push({
35386                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35387                 y : minY
35388             });
35389             
35390             return pos;
35391         }
35392         
35393         if(box[0].size == 'md-right'){
35394             pos.push({
35395                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35396                 y : minY + (this.unitWidth + this.gutter) * 1
35397             });
35398             
35399             return pos;
35400         }
35401         
35402         var rand = Math.floor(Math.random() * (4 - box[0].y));
35403         
35404         pos.push({
35405             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35406             y : minY + (this.unitWidth + this.gutter) * rand
35407         });
35408         
35409         return pos;
35410         
35411     },
35412     
35413     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35414     {
35415         var pos = [];
35416         
35417         if(box[0].size == 'xs'){
35418             
35419             pos.push({
35420                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35421                 y : minY
35422             });
35423
35424             pos.push({
35425                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35426                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35427             });
35428             
35429             return pos;
35430             
35431         }
35432         
35433         pos.push({
35434             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35435             y : minY
35436         });
35437
35438         pos.push({
35439             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35440             y : minY + (this.unitWidth + this.gutter) * 2
35441         });
35442         
35443         return pos;
35444         
35445     },
35446     
35447     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35448     {
35449         var pos = [];
35450         
35451         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35452             
35453             pos.push({
35454                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35455                 y : minY
35456             });
35457
35458             pos.push({
35459                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35460                 y : minY + (this.unitWidth + this.gutter) * 1
35461             });
35462             
35463             pos.push({
35464                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35465                 y : minY + (this.unitWidth + this.gutter) * 2
35466             });
35467             
35468             return pos;
35469             
35470         }
35471         
35472         if(box[0].size == 'xs' && box[1].size == 'xs'){
35473             
35474             pos.push({
35475                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35476                 y : minY
35477             });
35478
35479             pos.push({
35480                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35481                 y : minY
35482             });
35483             
35484             pos.push({
35485                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35486                 y : minY + (this.unitWidth + this.gutter) * 1
35487             });
35488             
35489             return pos;
35490             
35491         }
35492         
35493         pos.push({
35494             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35495             y : minY
35496         });
35497
35498         pos.push({
35499             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35500             y : minY + (this.unitWidth + this.gutter) * 2
35501         });
35502
35503         pos.push({
35504             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35505             y : minY + (this.unitWidth + this.gutter) * 2
35506         });
35507             
35508         return pos;
35509         
35510     },
35511     
35512     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35513     {
35514         var pos = [];
35515         
35516         if(box[0].size == 'xs'){
35517             
35518             pos.push({
35519                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35520                 y : minY
35521             });
35522
35523             pos.push({
35524                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35525                 y : minY
35526             });
35527             
35528             pos.push({
35529                 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),
35530                 y : minY
35531             });
35532             
35533             pos.push({
35534                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35535                 y : minY + (this.unitWidth + this.gutter) * 1
35536             });
35537             
35538             return pos;
35539             
35540         }
35541         
35542         pos.push({
35543             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35544             y : minY
35545         });
35546         
35547         pos.push({
35548             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35549             y : minY + (this.unitWidth + this.gutter) * 2
35550         });
35551         
35552         pos.push({
35553             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35554             y : minY + (this.unitWidth + this.gutter) * 2
35555         });
35556         
35557         pos.push({
35558             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),
35559             y : minY + (this.unitWidth + this.gutter) * 2
35560         });
35561
35562         return pos;
35563         
35564     },
35565     
35566     /**
35567     * remove a Masonry Brick
35568     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35569     */
35570     removeBrick : function(brick_id)
35571     {
35572         if (!brick_id) {
35573             return;
35574         }
35575         
35576         for (var i = 0; i<this.bricks.length; i++) {
35577             if (this.bricks[i].id == brick_id) {
35578                 this.bricks.splice(i,1);
35579                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35580                 this.initial();
35581             }
35582         }
35583     },
35584     
35585     /**
35586     * adds a Masonry Brick
35587     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35588     */
35589     addBrick : function(cfg)
35590     {
35591         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35592         //this.register(cn);
35593         cn.parentId = this.id;
35594         cn.render(this.el);
35595         return cn;
35596     },
35597     
35598     /**
35599     * register a Masonry Brick
35600     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35601     */
35602     
35603     register : function(brick)
35604     {
35605         this.bricks.push(brick);
35606         brick.masonryId = this.id;
35607     },
35608     
35609     /**
35610     * clear all the Masonry Brick
35611     */
35612     clearAll : function()
35613     {
35614         this.bricks = [];
35615         //this.getChildContainer().dom.innerHTML = "";
35616         this.el.dom.innerHTML = '';
35617     },
35618     
35619     getSelected : function()
35620     {
35621         if (!this.selectedBrick) {
35622             return false;
35623         }
35624         
35625         return this.selectedBrick;
35626     }
35627 });
35628
35629 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35630     
35631     groups: {},
35632      /**
35633     * register a Masonry Layout
35634     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35635     */
35636     
35637     register : function(layout)
35638     {
35639         this.groups[layout.id] = layout;
35640     },
35641     /**
35642     * fetch a  Masonry Layout based on the masonry layout ID
35643     * @param {string} the masonry layout to add
35644     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35645     */
35646     
35647     get: function(layout_id) {
35648         if (typeof(this.groups[layout_id]) == 'undefined') {
35649             return false;
35650         }
35651         return this.groups[layout_id] ;
35652     }
35653     
35654     
35655     
35656 });
35657
35658  
35659
35660  /**
35661  *
35662  * This is based on 
35663  * http://masonry.desandro.com
35664  *
35665  * The idea is to render all the bricks based on vertical width...
35666  *
35667  * The original code extends 'outlayer' - we might need to use that....
35668  * 
35669  */
35670
35671
35672 /**
35673  * @class Roo.bootstrap.LayoutMasonryAuto
35674  * @extends Roo.bootstrap.Component
35675  * Bootstrap Layout Masonry class
35676  * 
35677  * @constructor
35678  * Create a new Element
35679  * @param {Object} config The config object
35680  */
35681
35682 Roo.bootstrap.LayoutMasonryAuto = function(config){
35683     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35684 };
35685
35686 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35687     
35688       /**
35689      * @cfg {Boolean} isFitWidth  - resize the width..
35690      */   
35691     isFitWidth : false,  // options..
35692     /**
35693      * @cfg {Boolean} isOriginLeft = left align?
35694      */   
35695     isOriginLeft : true,
35696     /**
35697      * @cfg {Boolean} isOriginTop = top align?
35698      */   
35699     isOriginTop : false,
35700     /**
35701      * @cfg {Boolean} isLayoutInstant = no animation?
35702      */   
35703     isLayoutInstant : false, // needed?
35704     /**
35705      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35706      */   
35707     isResizingContainer : true,
35708     /**
35709      * @cfg {Number} columnWidth  width of the columns 
35710      */   
35711     
35712     columnWidth : 0,
35713     
35714     /**
35715      * @cfg {Number} maxCols maximum number of columns
35716      */   
35717     
35718     maxCols: 0,
35719     /**
35720      * @cfg {Number} padHeight padding below box..
35721      */   
35722     
35723     padHeight : 10, 
35724     
35725     /**
35726      * @cfg {Boolean} isAutoInitial defalut true
35727      */   
35728     
35729     isAutoInitial : true, 
35730     
35731     // private?
35732     gutter : 0,
35733     
35734     containerWidth: 0,
35735     initialColumnWidth : 0,
35736     currentSize : null,
35737     
35738     colYs : null, // array.
35739     maxY : 0,
35740     padWidth: 10,
35741     
35742     
35743     tag: 'div',
35744     cls: '',
35745     bricks: null, //CompositeElement
35746     cols : 0, // array?
35747     // element : null, // wrapped now this.el
35748     _isLayoutInited : null, 
35749     
35750     
35751     getAutoCreate : function(){
35752         
35753         var cfg = {
35754             tag: this.tag,
35755             cls: 'blog-masonary-wrapper ' + this.cls,
35756             cn : {
35757                 cls : 'mas-boxes masonary'
35758             }
35759         };
35760         
35761         return cfg;
35762     },
35763     
35764     getChildContainer: function( )
35765     {
35766         if (this.boxesEl) {
35767             return this.boxesEl;
35768         }
35769         
35770         this.boxesEl = this.el.select('.mas-boxes').first();
35771         
35772         return this.boxesEl;
35773     },
35774     
35775     
35776     initEvents : function()
35777     {
35778         var _this = this;
35779         
35780         if(this.isAutoInitial){
35781             Roo.log('hook children rendered');
35782             this.on('childrenrendered', function() {
35783                 Roo.log('children rendered');
35784                 _this.initial();
35785             } ,this);
35786         }
35787         
35788     },
35789     
35790     initial : function()
35791     {
35792         this.reloadItems();
35793
35794         this.currentSize = this.el.getBox(true);
35795
35796         /// was window resize... - let's see if this works..
35797         Roo.EventManager.onWindowResize(this.resize, this); 
35798
35799         if(!this.isAutoInitial){
35800             this.layout();
35801             return;
35802         }
35803         
35804         this.layout.defer(500,this);
35805     },
35806     
35807     reloadItems: function()
35808     {
35809         this.bricks = this.el.select('.masonry-brick', true);
35810         
35811         this.bricks.each(function(b) {
35812             //Roo.log(b.getSize());
35813             if (!b.attr('originalwidth')) {
35814                 b.attr('originalwidth',  b.getSize().width);
35815             }
35816             
35817         });
35818         
35819         Roo.log(this.bricks.elements.length);
35820     },
35821     
35822     resize : function()
35823     {
35824         Roo.log('resize');
35825         var cs = this.el.getBox(true);
35826         
35827         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35828             Roo.log("no change in with or X");
35829             return;
35830         }
35831         this.currentSize = cs;
35832         this.layout();
35833     },
35834     
35835     layout : function()
35836     {
35837          Roo.log('layout');
35838         this._resetLayout();
35839         //this._manageStamps();
35840       
35841         // don't animate first layout
35842         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35843         this.layoutItems( isInstant );
35844       
35845         // flag for initalized
35846         this._isLayoutInited = true;
35847     },
35848     
35849     layoutItems : function( isInstant )
35850     {
35851         //var items = this._getItemsForLayout( this.items );
35852         // original code supports filtering layout items.. we just ignore it..
35853         
35854         this._layoutItems( this.bricks , isInstant );
35855       
35856         this._postLayout();
35857     },
35858     _layoutItems : function ( items , isInstant)
35859     {
35860        //this.fireEvent( 'layout', this, items );
35861     
35862
35863         if ( !items || !items.elements.length ) {
35864           // no items, emit event with empty array
35865             return;
35866         }
35867
35868         var queue = [];
35869         items.each(function(item) {
35870             Roo.log("layout item");
35871             Roo.log(item);
35872             // get x/y object from method
35873             var position = this._getItemLayoutPosition( item );
35874             // enqueue
35875             position.item = item;
35876             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35877             queue.push( position );
35878         }, this);
35879       
35880         this._processLayoutQueue( queue );
35881     },
35882     /** Sets position of item in DOM
35883     * @param {Element} item
35884     * @param {Number} x - horizontal position
35885     * @param {Number} y - vertical position
35886     * @param {Boolean} isInstant - disables transitions
35887     */
35888     _processLayoutQueue : function( queue )
35889     {
35890         for ( var i=0, len = queue.length; i < len; i++ ) {
35891             var obj = queue[i];
35892             obj.item.position('absolute');
35893             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35894         }
35895     },
35896       
35897     
35898     /**
35899     * Any logic you want to do after each layout,
35900     * i.e. size the container
35901     */
35902     _postLayout : function()
35903     {
35904         this.resizeContainer();
35905     },
35906     
35907     resizeContainer : function()
35908     {
35909         if ( !this.isResizingContainer ) {
35910             return;
35911         }
35912         var size = this._getContainerSize();
35913         if ( size ) {
35914             this.el.setSize(size.width,size.height);
35915             this.boxesEl.setSize(size.width,size.height);
35916         }
35917     },
35918     
35919     
35920     
35921     _resetLayout : function()
35922     {
35923         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35924         this.colWidth = this.el.getWidth();
35925         //this.gutter = this.el.getWidth(); 
35926         
35927         this.measureColumns();
35928
35929         // reset column Y
35930         var i = this.cols;
35931         this.colYs = [];
35932         while (i--) {
35933             this.colYs.push( 0 );
35934         }
35935     
35936         this.maxY = 0;
35937     },
35938
35939     measureColumns : function()
35940     {
35941         this.getContainerWidth();
35942       // if columnWidth is 0, default to outerWidth of first item
35943         if ( !this.columnWidth ) {
35944             var firstItem = this.bricks.first();
35945             Roo.log(firstItem);
35946             this.columnWidth  = this.containerWidth;
35947             if (firstItem && firstItem.attr('originalwidth') ) {
35948                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35949             }
35950             // columnWidth fall back to item of first element
35951             Roo.log("set column width?");
35952                         this.initialColumnWidth = this.columnWidth  ;
35953
35954             // if first elem has no width, default to size of container
35955             
35956         }
35957         
35958         
35959         if (this.initialColumnWidth) {
35960             this.columnWidth = this.initialColumnWidth;
35961         }
35962         
35963         
35964             
35965         // column width is fixed at the top - however if container width get's smaller we should
35966         // reduce it...
35967         
35968         // this bit calcs how man columns..
35969             
35970         var columnWidth = this.columnWidth += this.gutter;
35971       
35972         // calculate columns
35973         var containerWidth = this.containerWidth + this.gutter;
35974         
35975         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35976         // fix rounding errors, typically with gutters
35977         var excess = columnWidth - containerWidth % columnWidth;
35978         
35979         
35980         // if overshoot is less than a pixel, round up, otherwise floor it
35981         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35982         cols = Math[ mathMethod ]( cols );
35983         this.cols = Math.max( cols, 1 );
35984         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35985         
35986          // padding positioning..
35987         var totalColWidth = this.cols * this.columnWidth;
35988         var padavail = this.containerWidth - totalColWidth;
35989         // so for 2 columns - we need 3 'pads'
35990         
35991         var padNeeded = (1+this.cols) * this.padWidth;
35992         
35993         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35994         
35995         this.columnWidth += padExtra
35996         //this.padWidth = Math.floor(padavail /  ( this.cols));
35997         
35998         // adjust colum width so that padding is fixed??
35999         
36000         // we have 3 columns ... total = width * 3
36001         // we have X left over... that should be used by 
36002         
36003         //if (this.expandC) {
36004             
36005         //}
36006         
36007         
36008         
36009     },
36010     
36011     getContainerWidth : function()
36012     {
36013        /* // container is parent if fit width
36014         var container = this.isFitWidth ? this.element.parentNode : this.element;
36015         // check that this.size and size are there
36016         // IE8 triggers resize on body size change, so they might not be
36017         
36018         var size = getSize( container );  //FIXME
36019         this.containerWidth = size && size.innerWidth; //FIXME
36020         */
36021          
36022         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36023         
36024     },
36025     
36026     _getItemLayoutPosition : function( item )  // what is item?
36027     {
36028         // we resize the item to our columnWidth..
36029       
36030         item.setWidth(this.columnWidth);
36031         item.autoBoxAdjust  = false;
36032         
36033         var sz = item.getSize();
36034  
36035         // how many columns does this brick span
36036         var remainder = this.containerWidth % this.columnWidth;
36037         
36038         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36039         // round if off by 1 pixel, otherwise use ceil
36040         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36041         colSpan = Math.min( colSpan, this.cols );
36042         
36043         // normally this should be '1' as we dont' currently allow multi width columns..
36044         
36045         var colGroup = this._getColGroup( colSpan );
36046         // get the minimum Y value from the columns
36047         var minimumY = Math.min.apply( Math, colGroup );
36048         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36049         
36050         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36051          
36052         // position the brick
36053         var position = {
36054             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36055             y: this.currentSize.y + minimumY + this.padHeight
36056         };
36057         
36058         Roo.log(position);
36059         // apply setHeight to necessary columns
36060         var setHeight = minimumY + sz.height + this.padHeight;
36061         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36062         
36063         var setSpan = this.cols + 1 - colGroup.length;
36064         for ( var i = 0; i < setSpan; i++ ) {
36065           this.colYs[ shortColIndex + i ] = setHeight ;
36066         }
36067       
36068         return position;
36069     },
36070     
36071     /**
36072      * @param {Number} colSpan - number of columns the element spans
36073      * @returns {Array} colGroup
36074      */
36075     _getColGroup : function( colSpan )
36076     {
36077         if ( colSpan < 2 ) {
36078           // if brick spans only one column, use all the column Ys
36079           return this.colYs;
36080         }
36081       
36082         var colGroup = [];
36083         // how many different places could this brick fit horizontally
36084         var groupCount = this.cols + 1 - colSpan;
36085         // for each group potential horizontal position
36086         for ( var i = 0; i < groupCount; i++ ) {
36087           // make an array of colY values for that one group
36088           var groupColYs = this.colYs.slice( i, i + colSpan );
36089           // and get the max value of the array
36090           colGroup[i] = Math.max.apply( Math, groupColYs );
36091         }
36092         return colGroup;
36093     },
36094     /*
36095     _manageStamp : function( stamp )
36096     {
36097         var stampSize =  stamp.getSize();
36098         var offset = stamp.getBox();
36099         // get the columns that this stamp affects
36100         var firstX = this.isOriginLeft ? offset.x : offset.right;
36101         var lastX = firstX + stampSize.width;
36102         var firstCol = Math.floor( firstX / this.columnWidth );
36103         firstCol = Math.max( 0, firstCol );
36104         
36105         var lastCol = Math.floor( lastX / this.columnWidth );
36106         // lastCol should not go over if multiple of columnWidth #425
36107         lastCol -= lastX % this.columnWidth ? 0 : 1;
36108         lastCol = Math.min( this.cols - 1, lastCol );
36109         
36110         // set colYs to bottom of the stamp
36111         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36112             stampSize.height;
36113             
36114         for ( var i = firstCol; i <= lastCol; i++ ) {
36115           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36116         }
36117     },
36118     */
36119     
36120     _getContainerSize : function()
36121     {
36122         this.maxY = Math.max.apply( Math, this.colYs );
36123         var size = {
36124             height: this.maxY
36125         };
36126       
36127         if ( this.isFitWidth ) {
36128             size.width = this._getContainerFitWidth();
36129         }
36130       
36131         return size;
36132     },
36133     
36134     _getContainerFitWidth : function()
36135     {
36136         var unusedCols = 0;
36137         // count unused columns
36138         var i = this.cols;
36139         while ( --i ) {
36140           if ( this.colYs[i] !== 0 ) {
36141             break;
36142           }
36143           unusedCols++;
36144         }
36145         // fit container to columns that have been used
36146         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36147     },
36148     
36149     needsResizeLayout : function()
36150     {
36151         var previousWidth = this.containerWidth;
36152         this.getContainerWidth();
36153         return previousWidth !== this.containerWidth;
36154     }
36155  
36156 });
36157
36158  
36159
36160  /*
36161  * - LGPL
36162  *
36163  * element
36164  * 
36165  */
36166
36167 /**
36168  * @class Roo.bootstrap.MasonryBrick
36169  * @extends Roo.bootstrap.Component
36170  * Bootstrap MasonryBrick class
36171  * 
36172  * @constructor
36173  * Create a new MasonryBrick
36174  * @param {Object} config The config object
36175  */
36176
36177 Roo.bootstrap.MasonryBrick = function(config){
36178     
36179     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36180     
36181     Roo.bootstrap.MasonryBrick.register(this);
36182     
36183     this.addEvents({
36184         // raw events
36185         /**
36186          * @event click
36187          * When a MasonryBrick is clcik
36188          * @param {Roo.bootstrap.MasonryBrick} this
36189          * @param {Roo.EventObject} e
36190          */
36191         "click" : true
36192     });
36193 };
36194
36195 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36196     
36197     /**
36198      * @cfg {String} title
36199      */   
36200     title : '',
36201     /**
36202      * @cfg {String} html
36203      */   
36204     html : '',
36205     /**
36206      * @cfg {String} bgimage
36207      */   
36208     bgimage : '',
36209     /**
36210      * @cfg {String} videourl
36211      */   
36212     videourl : '',
36213     /**
36214      * @cfg {String} cls
36215      */   
36216     cls : '',
36217     /**
36218      * @cfg {String} href
36219      */   
36220     href : '',
36221     /**
36222      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36223      */   
36224     size : 'xs',
36225     
36226     /**
36227      * @cfg {String} placetitle (center|bottom)
36228      */   
36229     placetitle : '',
36230     
36231     /**
36232      * @cfg {Boolean} isFitContainer defalut true
36233      */   
36234     isFitContainer : true, 
36235     
36236     /**
36237      * @cfg {Boolean} preventDefault defalut false
36238      */   
36239     preventDefault : false, 
36240     
36241     /**
36242      * @cfg {Boolean} inverse defalut false
36243      */   
36244     maskInverse : false, 
36245     
36246     getAutoCreate : function()
36247     {
36248         if(!this.isFitContainer){
36249             return this.getSplitAutoCreate();
36250         }
36251         
36252         var cls = 'masonry-brick masonry-brick-full';
36253         
36254         if(this.href.length){
36255             cls += ' masonry-brick-link';
36256         }
36257         
36258         if(this.bgimage.length){
36259             cls += ' masonry-brick-image';
36260         }
36261         
36262         if(this.maskInverse){
36263             cls += ' mask-inverse';
36264         }
36265         
36266         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36267             cls += ' enable-mask';
36268         }
36269         
36270         if(this.size){
36271             cls += ' masonry-' + this.size + '-brick';
36272         }
36273         
36274         if(this.placetitle.length){
36275             
36276             switch (this.placetitle) {
36277                 case 'center' :
36278                     cls += ' masonry-center-title';
36279                     break;
36280                 case 'bottom' :
36281                     cls += ' masonry-bottom-title';
36282                     break;
36283                 default:
36284                     break;
36285             }
36286             
36287         } else {
36288             if(!this.html.length && !this.bgimage.length){
36289                 cls += ' masonry-center-title';
36290             }
36291
36292             if(!this.html.length && this.bgimage.length){
36293                 cls += ' masonry-bottom-title';
36294             }
36295         }
36296         
36297         if(this.cls){
36298             cls += ' ' + this.cls;
36299         }
36300         
36301         var cfg = {
36302             tag: (this.href.length) ? 'a' : 'div',
36303             cls: cls,
36304             cn: [
36305                 {
36306                     tag: 'div',
36307                     cls: 'masonry-brick-mask'
36308                 },
36309                 {
36310                     tag: 'div',
36311                     cls: 'masonry-brick-paragraph',
36312                     cn: []
36313                 }
36314             ]
36315         };
36316         
36317         if(this.href.length){
36318             cfg.href = this.href;
36319         }
36320         
36321         var cn = cfg.cn[1].cn;
36322         
36323         if(this.title.length){
36324             cn.push({
36325                 tag: 'h4',
36326                 cls: 'masonry-brick-title',
36327                 html: this.title
36328             });
36329         }
36330         
36331         if(this.html.length){
36332             cn.push({
36333                 tag: 'p',
36334                 cls: 'masonry-brick-text',
36335                 html: this.html
36336             });
36337         }
36338         
36339         if (!this.title.length && !this.html.length) {
36340             cfg.cn[1].cls += ' hide';
36341         }
36342         
36343         if(this.bgimage.length){
36344             cfg.cn.push({
36345                 tag: 'img',
36346                 cls: 'masonry-brick-image-view',
36347                 src: this.bgimage
36348             });
36349         }
36350         
36351         if(this.videourl.length){
36352             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36353             // youtube support only?
36354             cfg.cn.push({
36355                 tag: 'iframe',
36356                 cls: 'masonry-brick-image-view',
36357                 src: vurl,
36358                 frameborder : 0,
36359                 allowfullscreen : true
36360             });
36361         }
36362         
36363         return cfg;
36364         
36365     },
36366     
36367     getSplitAutoCreate : function()
36368     {
36369         var cls = 'masonry-brick masonry-brick-split';
36370         
36371         if(this.href.length){
36372             cls += ' masonry-brick-link';
36373         }
36374         
36375         if(this.bgimage.length){
36376             cls += ' masonry-brick-image';
36377         }
36378         
36379         if(this.size){
36380             cls += ' masonry-' + this.size + '-brick';
36381         }
36382         
36383         switch (this.placetitle) {
36384             case 'center' :
36385                 cls += ' masonry-center-title';
36386                 break;
36387             case 'bottom' :
36388                 cls += ' masonry-bottom-title';
36389                 break;
36390             default:
36391                 if(!this.bgimage.length){
36392                     cls += ' masonry-center-title';
36393                 }
36394
36395                 if(this.bgimage.length){
36396                     cls += ' masonry-bottom-title';
36397                 }
36398                 break;
36399         }
36400         
36401         if(this.cls){
36402             cls += ' ' + this.cls;
36403         }
36404         
36405         var cfg = {
36406             tag: (this.href.length) ? 'a' : 'div',
36407             cls: cls,
36408             cn: [
36409                 {
36410                     tag: 'div',
36411                     cls: 'masonry-brick-split-head',
36412                     cn: [
36413                         {
36414                             tag: 'div',
36415                             cls: 'masonry-brick-paragraph',
36416                             cn: []
36417                         }
36418                     ]
36419                 },
36420                 {
36421                     tag: 'div',
36422                     cls: 'masonry-brick-split-body',
36423                     cn: []
36424                 }
36425             ]
36426         };
36427         
36428         if(this.href.length){
36429             cfg.href = this.href;
36430         }
36431         
36432         if(this.title.length){
36433             cfg.cn[0].cn[0].cn.push({
36434                 tag: 'h4',
36435                 cls: 'masonry-brick-title',
36436                 html: this.title
36437             });
36438         }
36439         
36440         if(this.html.length){
36441             cfg.cn[1].cn.push({
36442                 tag: 'p',
36443                 cls: 'masonry-brick-text',
36444                 html: this.html
36445             });
36446         }
36447
36448         if(this.bgimage.length){
36449             cfg.cn[0].cn.push({
36450                 tag: 'img',
36451                 cls: 'masonry-brick-image-view',
36452                 src: this.bgimage
36453             });
36454         }
36455         
36456         if(this.videourl.length){
36457             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36458             // youtube support only?
36459             cfg.cn[0].cn.cn.push({
36460                 tag: 'iframe',
36461                 cls: 'masonry-brick-image-view',
36462                 src: vurl,
36463                 frameborder : 0,
36464                 allowfullscreen : true
36465             });
36466         }
36467         
36468         return cfg;
36469     },
36470     
36471     initEvents: function() 
36472     {
36473         switch (this.size) {
36474             case 'xs' :
36475                 this.x = 1;
36476                 this.y = 1;
36477                 break;
36478             case 'sm' :
36479                 this.x = 2;
36480                 this.y = 2;
36481                 break;
36482             case 'md' :
36483             case 'md-left' :
36484             case 'md-right' :
36485                 this.x = 3;
36486                 this.y = 3;
36487                 break;
36488             case 'tall' :
36489                 this.x = 2;
36490                 this.y = 3;
36491                 break;
36492             case 'wide' :
36493                 this.x = 3;
36494                 this.y = 2;
36495                 break;
36496             case 'wide-thin' :
36497                 this.x = 3;
36498                 this.y = 1;
36499                 break;
36500                         
36501             default :
36502                 break;
36503         }
36504         
36505         if(Roo.isTouch){
36506             this.el.on('touchstart', this.onTouchStart, this);
36507             this.el.on('touchmove', this.onTouchMove, this);
36508             this.el.on('touchend', this.onTouchEnd, this);
36509             this.el.on('contextmenu', this.onContextMenu, this);
36510         } else {
36511             this.el.on('mouseenter'  ,this.enter, this);
36512             this.el.on('mouseleave', this.leave, this);
36513             this.el.on('click', this.onClick, this);
36514         }
36515         
36516         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36517             this.parent().bricks.push(this);   
36518         }
36519         
36520     },
36521     
36522     onClick: function(e, el)
36523     {
36524         var time = this.endTimer - this.startTimer;
36525         // Roo.log(e.preventDefault());
36526         if(Roo.isTouch){
36527             if(time > 1000){
36528                 e.preventDefault();
36529                 return;
36530             }
36531         }
36532         
36533         if(!this.preventDefault){
36534             return;
36535         }
36536         
36537         e.preventDefault();
36538         
36539         if (this.activeClass != '') {
36540             this.selectBrick();
36541         }
36542         
36543         this.fireEvent('click', this, e);
36544     },
36545     
36546     enter: function(e, el)
36547     {
36548         e.preventDefault();
36549         
36550         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36551             return;
36552         }
36553         
36554         if(this.bgimage.length && this.html.length){
36555             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36556         }
36557     },
36558     
36559     leave: function(e, el)
36560     {
36561         e.preventDefault();
36562         
36563         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36564             return;
36565         }
36566         
36567         if(this.bgimage.length && this.html.length){
36568             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36569         }
36570     },
36571     
36572     onTouchStart: function(e, el)
36573     {
36574 //        e.preventDefault();
36575         
36576         this.touchmoved = false;
36577         
36578         if(!this.isFitContainer){
36579             return;
36580         }
36581         
36582         if(!this.bgimage.length || !this.html.length){
36583             return;
36584         }
36585         
36586         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36587         
36588         this.timer = new Date().getTime();
36589         
36590     },
36591     
36592     onTouchMove: function(e, el)
36593     {
36594         this.touchmoved = true;
36595     },
36596     
36597     onContextMenu : function(e,el)
36598     {
36599         e.preventDefault();
36600         e.stopPropagation();
36601         return false;
36602     },
36603     
36604     onTouchEnd: function(e, el)
36605     {
36606 //        e.preventDefault();
36607         
36608         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36609         
36610             this.leave(e,el);
36611             
36612             return;
36613         }
36614         
36615         if(!this.bgimage.length || !this.html.length){
36616             
36617             if(this.href.length){
36618                 window.location.href = this.href;
36619             }
36620             
36621             return;
36622         }
36623         
36624         if(!this.isFitContainer){
36625             return;
36626         }
36627         
36628         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36629         
36630         window.location.href = this.href;
36631     },
36632     
36633     //selection on single brick only
36634     selectBrick : function() {
36635         
36636         if (!this.parentId) {
36637             return;
36638         }
36639         
36640         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36641         var index = m.selectedBrick.indexOf(this.id);
36642         
36643         if ( index > -1) {
36644             m.selectedBrick.splice(index,1);
36645             this.el.removeClass(this.activeClass);
36646             return;
36647         }
36648         
36649         for(var i = 0; i < m.selectedBrick.length; i++) {
36650             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36651             b.el.removeClass(b.activeClass);
36652         }
36653         
36654         m.selectedBrick = [];
36655         
36656         m.selectedBrick.push(this.id);
36657         this.el.addClass(this.activeClass);
36658         return;
36659     },
36660     
36661     isSelected : function(){
36662         return this.el.hasClass(this.activeClass);
36663         
36664     }
36665 });
36666
36667 Roo.apply(Roo.bootstrap.MasonryBrick, {
36668     
36669     //groups: {},
36670     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36671      /**
36672     * register a Masonry Brick
36673     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36674     */
36675     
36676     register : function(brick)
36677     {
36678         //this.groups[brick.id] = brick;
36679         this.groups.add(brick.id, brick);
36680     },
36681     /**
36682     * fetch a  masonry brick based on the masonry brick ID
36683     * @param {string} the masonry brick to add
36684     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36685     */
36686     
36687     get: function(brick_id) 
36688     {
36689         // if (typeof(this.groups[brick_id]) == 'undefined') {
36690         //     return false;
36691         // }
36692         // return this.groups[brick_id] ;
36693         
36694         if(this.groups.key(brick_id)) {
36695             return this.groups.key(brick_id);
36696         }
36697         
36698         return false;
36699     }
36700     
36701     
36702     
36703 });
36704
36705  /*
36706  * - LGPL
36707  *
36708  * element
36709  * 
36710  */
36711
36712 /**
36713  * @class Roo.bootstrap.Brick
36714  * @extends Roo.bootstrap.Component
36715  * Bootstrap Brick class
36716  * 
36717  * @constructor
36718  * Create a new Brick
36719  * @param {Object} config The config object
36720  */
36721
36722 Roo.bootstrap.Brick = function(config){
36723     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36724     
36725     this.addEvents({
36726         // raw events
36727         /**
36728          * @event click
36729          * When a Brick is click
36730          * @param {Roo.bootstrap.Brick} this
36731          * @param {Roo.EventObject} e
36732          */
36733         "click" : true
36734     });
36735 };
36736
36737 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36738     
36739     /**
36740      * @cfg {String} title
36741      */   
36742     title : '',
36743     /**
36744      * @cfg {String} html
36745      */   
36746     html : '',
36747     /**
36748      * @cfg {String} bgimage
36749      */   
36750     bgimage : '',
36751     /**
36752      * @cfg {String} cls
36753      */   
36754     cls : '',
36755     /**
36756      * @cfg {String} href
36757      */   
36758     href : '',
36759     /**
36760      * @cfg {String} video
36761      */   
36762     video : '',
36763     /**
36764      * @cfg {Boolean} square
36765      */   
36766     square : true,
36767     
36768     getAutoCreate : function()
36769     {
36770         var cls = 'roo-brick';
36771         
36772         if(this.href.length){
36773             cls += ' roo-brick-link';
36774         }
36775         
36776         if(this.bgimage.length){
36777             cls += ' roo-brick-image';
36778         }
36779         
36780         if(!this.html.length && !this.bgimage.length){
36781             cls += ' roo-brick-center-title';
36782         }
36783         
36784         if(!this.html.length && this.bgimage.length){
36785             cls += ' roo-brick-bottom-title';
36786         }
36787         
36788         if(this.cls){
36789             cls += ' ' + this.cls;
36790         }
36791         
36792         var cfg = {
36793             tag: (this.href.length) ? 'a' : 'div',
36794             cls: cls,
36795             cn: [
36796                 {
36797                     tag: 'div',
36798                     cls: 'roo-brick-paragraph',
36799                     cn: []
36800                 }
36801             ]
36802         };
36803         
36804         if(this.href.length){
36805             cfg.href = this.href;
36806         }
36807         
36808         var cn = cfg.cn[0].cn;
36809         
36810         if(this.title.length){
36811             cn.push({
36812                 tag: 'h4',
36813                 cls: 'roo-brick-title',
36814                 html: this.title
36815             });
36816         }
36817         
36818         if(this.html.length){
36819             cn.push({
36820                 tag: 'p',
36821                 cls: 'roo-brick-text',
36822                 html: this.html
36823             });
36824         } else {
36825             cn.cls += ' hide';
36826         }
36827         
36828         if(this.bgimage.length){
36829             cfg.cn.push({
36830                 tag: 'img',
36831                 cls: 'roo-brick-image-view',
36832                 src: this.bgimage
36833             });
36834         }
36835         
36836         return cfg;
36837     },
36838     
36839     initEvents: function() 
36840     {
36841         if(this.title.length || this.html.length){
36842             this.el.on('mouseenter'  ,this.enter, this);
36843             this.el.on('mouseleave', this.leave, this);
36844         }
36845         
36846         Roo.EventManager.onWindowResize(this.resize, this); 
36847         
36848         if(this.bgimage.length){
36849             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36850             this.imageEl.on('load', this.onImageLoad, this);
36851             return;
36852         }
36853         
36854         this.resize();
36855     },
36856     
36857     onImageLoad : function()
36858     {
36859         this.resize();
36860     },
36861     
36862     resize : function()
36863     {
36864         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36865         
36866         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36867         
36868         if(this.bgimage.length){
36869             var image = this.el.select('.roo-brick-image-view', true).first();
36870             
36871             image.setWidth(paragraph.getWidth());
36872             
36873             if(this.square){
36874                 image.setHeight(paragraph.getWidth());
36875             }
36876             
36877             this.el.setHeight(image.getHeight());
36878             paragraph.setHeight(image.getHeight());
36879             
36880         }
36881         
36882     },
36883     
36884     enter: function(e, el)
36885     {
36886         e.preventDefault();
36887         
36888         if(this.bgimage.length){
36889             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36890             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36891         }
36892     },
36893     
36894     leave: function(e, el)
36895     {
36896         e.preventDefault();
36897         
36898         if(this.bgimage.length){
36899             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36900             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36901         }
36902     }
36903     
36904 });
36905
36906  
36907
36908  /*
36909  * - LGPL
36910  *
36911  * Number field 
36912  */
36913
36914 /**
36915  * @class Roo.bootstrap.NumberField
36916  * @extends Roo.bootstrap.Input
36917  * Bootstrap NumberField class
36918  * 
36919  * 
36920  * 
36921  * 
36922  * @constructor
36923  * Create a new NumberField
36924  * @param {Object} config The config object
36925  */
36926
36927 Roo.bootstrap.NumberField = function(config){
36928     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36929 };
36930
36931 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36932     
36933     /**
36934      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36935      */
36936     allowDecimals : true,
36937     /**
36938      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36939      */
36940     decimalSeparator : ".",
36941     /**
36942      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36943      */
36944     decimalPrecision : 2,
36945     /**
36946      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36947      */
36948     allowNegative : true,
36949     
36950     /**
36951      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36952      */
36953     allowZero: true,
36954     /**
36955      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36956      */
36957     minValue : Number.NEGATIVE_INFINITY,
36958     /**
36959      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36960      */
36961     maxValue : Number.MAX_VALUE,
36962     /**
36963      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36964      */
36965     minText : "The minimum value for this field is {0}",
36966     /**
36967      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36968      */
36969     maxText : "The maximum value for this field is {0}",
36970     /**
36971      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36972      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36973      */
36974     nanText : "{0} is not a valid number",
36975     /**
36976      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36977      */
36978     thousandsDelimiter : false,
36979     /**
36980      * @cfg {String} valueAlign alignment of value
36981      */
36982     valueAlign : "left",
36983
36984     getAutoCreate : function()
36985     {
36986         var hiddenInput = {
36987             tag: 'input',
36988             type: 'hidden',
36989             id: Roo.id(),
36990             cls: 'hidden-number-input'
36991         };
36992         
36993         if (this.name) {
36994             hiddenInput.name = this.name;
36995         }
36996         
36997         this.name = '';
36998         
36999         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37000         
37001         this.name = hiddenInput.name;
37002         
37003         if(cfg.cn.length > 0) {
37004             cfg.cn.push(hiddenInput);
37005         }
37006         
37007         return cfg;
37008     },
37009
37010     // private
37011     initEvents : function()
37012     {   
37013         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37014         
37015         var allowed = "0123456789";
37016         
37017         if(this.allowDecimals){
37018             allowed += this.decimalSeparator;
37019         }
37020         
37021         if(this.allowNegative){
37022             allowed += "-";
37023         }
37024         
37025         if(this.thousandsDelimiter) {
37026             allowed += ",";
37027         }
37028         
37029         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37030         
37031         var keyPress = function(e){
37032             
37033             var k = e.getKey();
37034             
37035             var c = e.getCharCode();
37036             
37037             if(
37038                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37039                     allowed.indexOf(String.fromCharCode(c)) === -1
37040             ){
37041                 e.stopEvent();
37042                 return;
37043             }
37044             
37045             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37046                 return;
37047             }
37048             
37049             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37050                 e.stopEvent();
37051             }
37052         };
37053         
37054         this.el.on("keypress", keyPress, this);
37055     },
37056     
37057     validateValue : function(value)
37058     {
37059         
37060         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37061             return false;
37062         }
37063         
37064         var num = this.parseValue(value);
37065         
37066         if(isNaN(num)){
37067             this.markInvalid(String.format(this.nanText, value));
37068             return false;
37069         }
37070         
37071         if(num < this.minValue){
37072             this.markInvalid(String.format(this.minText, this.minValue));
37073             return false;
37074         }
37075         
37076         if(num > this.maxValue){
37077             this.markInvalid(String.format(this.maxText, this.maxValue));
37078             return false;
37079         }
37080         
37081         return true;
37082     },
37083
37084     getValue : function()
37085     {
37086         var v = this.hiddenEl().getValue();
37087         
37088         return this.fixPrecision(this.parseValue(v));
37089     },
37090
37091     parseValue : function(value)
37092     {
37093         if(this.thousandsDelimiter) {
37094             value += "";
37095             r = new RegExp(",", "g");
37096             value = value.replace(r, "");
37097         }
37098         
37099         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37100         return isNaN(value) ? '' : value;
37101     },
37102
37103     fixPrecision : function(value)
37104     {
37105         if(this.thousandsDelimiter) {
37106             value += "";
37107             r = new RegExp(",", "g");
37108             value = value.replace(r, "");
37109         }
37110         
37111         var nan = isNaN(value);
37112         
37113         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37114             return nan ? '' : value;
37115         }
37116         return parseFloat(value).toFixed(this.decimalPrecision);
37117     },
37118
37119     setValue : function(v)
37120     {
37121         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37122         
37123         this.value = v;
37124         
37125         if(this.rendered){
37126             
37127             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37128             
37129             this.inputEl().dom.value = (v == '') ? '' :
37130                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37131             
37132             if(!this.allowZero && v === '0') {
37133                 this.hiddenEl().dom.value = '';
37134                 this.inputEl().dom.value = '';
37135             }
37136             
37137             this.validate();
37138         }
37139     },
37140
37141     decimalPrecisionFcn : function(v)
37142     {
37143         return Math.floor(v);
37144     },
37145
37146     beforeBlur : function()
37147     {
37148         var v = this.parseValue(this.getRawValue());
37149         
37150         if(v || v === 0 || v === ''){
37151             this.setValue(v);
37152         }
37153     },
37154     
37155     hiddenEl : function()
37156     {
37157         return this.el.select('input.hidden-number-input',true).first();
37158     }
37159     
37160 });
37161
37162  
37163
37164 /*
37165 * Licence: LGPL
37166 */
37167
37168 /**
37169  * @class Roo.bootstrap.DocumentSlider
37170  * @extends Roo.bootstrap.Component
37171  * Bootstrap DocumentSlider class
37172  * 
37173  * @constructor
37174  * Create a new DocumentViewer
37175  * @param {Object} config The config object
37176  */
37177
37178 Roo.bootstrap.DocumentSlider = function(config){
37179     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37180     
37181     this.files = [];
37182     
37183     this.addEvents({
37184         /**
37185          * @event initial
37186          * Fire after initEvent
37187          * @param {Roo.bootstrap.DocumentSlider} this
37188          */
37189         "initial" : true,
37190         /**
37191          * @event update
37192          * Fire after update
37193          * @param {Roo.bootstrap.DocumentSlider} this
37194          */
37195         "update" : true,
37196         /**
37197          * @event click
37198          * Fire after click
37199          * @param {Roo.bootstrap.DocumentSlider} this
37200          */
37201         "click" : true
37202     });
37203 };
37204
37205 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37206     
37207     files : false,
37208     
37209     indicator : 0,
37210     
37211     getAutoCreate : function()
37212     {
37213         var cfg = {
37214             tag : 'div',
37215             cls : 'roo-document-slider',
37216             cn : [
37217                 {
37218                     tag : 'div',
37219                     cls : 'roo-document-slider-header',
37220                     cn : [
37221                         {
37222                             tag : 'div',
37223                             cls : 'roo-document-slider-header-title'
37224                         }
37225                     ]
37226                 },
37227                 {
37228                     tag : 'div',
37229                     cls : 'roo-document-slider-body',
37230                     cn : [
37231                         {
37232                             tag : 'div',
37233                             cls : 'roo-document-slider-prev',
37234                             cn : [
37235                                 {
37236                                     tag : 'i',
37237                                     cls : 'fa fa-chevron-left'
37238                                 }
37239                             ]
37240                         },
37241                         {
37242                             tag : 'div',
37243                             cls : 'roo-document-slider-thumb',
37244                             cn : [
37245                                 {
37246                                     tag : 'img',
37247                                     cls : 'roo-document-slider-image'
37248                                 }
37249                             ]
37250                         },
37251                         {
37252                             tag : 'div',
37253                             cls : 'roo-document-slider-next',
37254                             cn : [
37255                                 {
37256                                     tag : 'i',
37257                                     cls : 'fa fa-chevron-right'
37258                                 }
37259                             ]
37260                         }
37261                     ]
37262                 }
37263             ]
37264         };
37265         
37266         return cfg;
37267     },
37268     
37269     initEvents : function()
37270     {
37271         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37272         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37273         
37274         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37275         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37276         
37277         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37278         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37279         
37280         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37281         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37282         
37283         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37284         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37285         
37286         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37287         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37288         
37289         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37290         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37291         
37292         this.thumbEl.on('click', this.onClick, this);
37293         
37294         this.prevIndicator.on('click', this.prev, this);
37295         
37296         this.nextIndicator.on('click', this.next, this);
37297         
37298     },
37299     
37300     initial : function()
37301     {
37302         if(this.files.length){
37303             this.indicator = 1;
37304             this.update()
37305         }
37306         
37307         this.fireEvent('initial', this);
37308     },
37309     
37310     update : function()
37311     {
37312         this.imageEl.attr('src', this.files[this.indicator - 1]);
37313         
37314         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37315         
37316         this.prevIndicator.show();
37317         
37318         if(this.indicator == 1){
37319             this.prevIndicator.hide();
37320         }
37321         
37322         this.nextIndicator.show();
37323         
37324         if(this.indicator == this.files.length){
37325             this.nextIndicator.hide();
37326         }
37327         
37328         this.thumbEl.scrollTo('top');
37329         
37330         this.fireEvent('update', this);
37331     },
37332     
37333     onClick : function(e)
37334     {
37335         e.preventDefault();
37336         
37337         this.fireEvent('click', this);
37338     },
37339     
37340     prev : function(e)
37341     {
37342         e.preventDefault();
37343         
37344         this.indicator = Math.max(1, this.indicator - 1);
37345         
37346         this.update();
37347     },
37348     
37349     next : function(e)
37350     {
37351         e.preventDefault();
37352         
37353         this.indicator = Math.min(this.files.length, this.indicator + 1);
37354         
37355         this.update();
37356     }
37357 });
37358 /*
37359  * - LGPL
37360  *
37361  * RadioSet
37362  *
37363  *
37364  */
37365
37366 /**
37367  * @class Roo.bootstrap.RadioSet
37368  * @extends Roo.bootstrap.Input
37369  * Bootstrap RadioSet class
37370  * @cfg {String} indicatorpos (left|right) default left
37371  * @cfg {Boolean} inline (true|false) inline the element (default true)
37372  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37373  * @constructor
37374  * Create a new RadioSet
37375  * @param {Object} config The config object
37376  */
37377
37378 Roo.bootstrap.RadioSet = function(config){
37379     
37380     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37381     
37382     this.radioes = [];
37383     
37384     Roo.bootstrap.RadioSet.register(this);
37385     
37386     this.addEvents({
37387         /**
37388         * @event check
37389         * Fires when the element is checked or unchecked.
37390         * @param {Roo.bootstrap.RadioSet} this This radio
37391         * @param {Roo.bootstrap.Radio} item The checked item
37392         */
37393        check : true,
37394        /**
37395         * @event click
37396         * Fires when the element is click.
37397         * @param {Roo.bootstrap.RadioSet} this This radio set
37398         * @param {Roo.bootstrap.Radio} item The checked item
37399         * @param {Roo.EventObject} e The event object
37400         */
37401        click : true
37402     });
37403     
37404 };
37405
37406 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37407
37408     radioes : false,
37409     
37410     inline : true,
37411     
37412     weight : '',
37413     
37414     indicatorpos : 'left',
37415     
37416     getAutoCreate : function()
37417     {
37418         var label = {
37419             tag : 'label',
37420             cls : 'roo-radio-set-label',
37421             cn : [
37422                 {
37423                     tag : 'span',
37424                     html : this.fieldLabel
37425                 }
37426             ]
37427         };
37428         if (Roo.bootstrap.version == 3) {
37429             
37430             
37431             if(this.indicatorpos == 'left'){
37432                 label.cn.unshift({
37433                     tag : 'i',
37434                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37435                     tooltip : 'This field is required'
37436                 });
37437             } else {
37438                 label.cn.push({
37439                     tag : 'i',
37440                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37441                     tooltip : 'This field is required'
37442                 });
37443             }
37444         }
37445         var items = {
37446             tag : 'div',
37447             cls : 'roo-radio-set-items'
37448         };
37449         
37450         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37451         
37452         if (align === 'left' && this.fieldLabel.length) {
37453             
37454             items = {
37455                 cls : "roo-radio-set-right", 
37456                 cn: [
37457                     items
37458                 ]
37459             };
37460             
37461             if(this.labelWidth > 12){
37462                 label.style = "width: " + this.labelWidth + 'px';
37463             }
37464             
37465             if(this.labelWidth < 13 && this.labelmd == 0){
37466                 this.labelmd = this.labelWidth;
37467             }
37468             
37469             if(this.labellg > 0){
37470                 label.cls += ' col-lg-' + this.labellg;
37471                 items.cls += ' col-lg-' + (12 - this.labellg);
37472             }
37473             
37474             if(this.labelmd > 0){
37475                 label.cls += ' col-md-' + this.labelmd;
37476                 items.cls += ' col-md-' + (12 - this.labelmd);
37477             }
37478             
37479             if(this.labelsm > 0){
37480                 label.cls += ' col-sm-' + this.labelsm;
37481                 items.cls += ' col-sm-' + (12 - this.labelsm);
37482             }
37483             
37484             if(this.labelxs > 0){
37485                 label.cls += ' col-xs-' + this.labelxs;
37486                 items.cls += ' col-xs-' + (12 - this.labelxs);
37487             }
37488         }
37489         
37490         var cfg = {
37491             tag : 'div',
37492             cls : 'roo-radio-set',
37493             cn : [
37494                 {
37495                     tag : 'input',
37496                     cls : 'roo-radio-set-input',
37497                     type : 'hidden',
37498                     name : this.name,
37499                     value : this.value ? this.value :  ''
37500                 },
37501                 label,
37502                 items
37503             ]
37504         };
37505         
37506         if(this.weight.length){
37507             cfg.cls += ' roo-radio-' + this.weight;
37508         }
37509         
37510         if(this.inline) {
37511             cfg.cls += ' roo-radio-set-inline';
37512         }
37513         
37514         var settings=this;
37515         ['xs','sm','md','lg'].map(function(size){
37516             if (settings[size]) {
37517                 cfg.cls += ' col-' + size + '-' + settings[size];
37518             }
37519         });
37520         
37521         return cfg;
37522         
37523     },
37524
37525     initEvents : function()
37526     {
37527         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37528         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37529         
37530         if(!this.fieldLabel.length){
37531             this.labelEl.hide();
37532         }
37533         
37534         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37535         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37536         
37537         this.indicator = this.indicatorEl();
37538         
37539         if(this.indicator){
37540             this.indicator.addClass('invisible');
37541         }
37542         
37543         this.originalValue = this.getValue();
37544         
37545     },
37546     
37547     inputEl: function ()
37548     {
37549         return this.el.select('.roo-radio-set-input', true).first();
37550     },
37551     
37552     getChildContainer : function()
37553     {
37554         return this.itemsEl;
37555     },
37556     
37557     register : function(item)
37558     {
37559         this.radioes.push(item);
37560         
37561     },
37562     
37563     validate : function()
37564     {   
37565         if(this.getVisibilityEl().hasClass('hidden')){
37566             return true;
37567         }
37568         
37569         var valid = false;
37570         
37571         Roo.each(this.radioes, function(i){
37572             if(!i.checked){
37573                 return;
37574             }
37575             
37576             valid = true;
37577             return false;
37578         });
37579         
37580         if(this.allowBlank) {
37581             return true;
37582         }
37583         
37584         if(this.disabled || valid){
37585             this.markValid();
37586             return true;
37587         }
37588         
37589         this.markInvalid();
37590         return false;
37591         
37592     },
37593     
37594     markValid : function()
37595     {
37596         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37597             this.indicatorEl().removeClass('visible');
37598             this.indicatorEl().addClass('invisible');
37599         }
37600         
37601         
37602         if (Roo.bootstrap.version == 3) {
37603             this.el.removeClass([this.invalidClass, this.validClass]);
37604             this.el.addClass(this.validClass);
37605         } else {
37606             this.el.removeClass(['is-invalid','is-valid']);
37607             this.el.addClass(['is-valid']);
37608         }
37609         this.fireEvent('valid', this);
37610     },
37611     
37612     markInvalid : function(msg)
37613     {
37614         if(this.allowBlank || this.disabled){
37615             return;
37616         }
37617         
37618         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37619             this.indicatorEl().removeClass('invisible');
37620             this.indicatorEl().addClass('visible');
37621         }
37622         if (Roo.bootstrap.version == 3) {
37623             this.el.removeClass([this.invalidClass, this.validClass]);
37624             this.el.addClass(this.invalidClass);
37625         } else {
37626             this.el.removeClass(['is-invalid','is-valid']);
37627             this.el.addClass(['is-invalid']);
37628         }
37629         
37630         this.fireEvent('invalid', this, msg);
37631         
37632     },
37633     
37634     setValue : function(v, suppressEvent)
37635     {   
37636         if(this.value === v){
37637             return;
37638         }
37639         
37640         this.value = v;
37641         
37642         if(this.rendered){
37643             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37644         }
37645         
37646         Roo.each(this.radioes, function(i){
37647             i.checked = false;
37648             i.el.removeClass('checked');
37649         });
37650         
37651         Roo.each(this.radioes, function(i){
37652             
37653             if(i.value === v || i.value.toString() === v.toString()){
37654                 i.checked = true;
37655                 i.el.addClass('checked');
37656                 
37657                 if(suppressEvent !== true){
37658                     this.fireEvent('check', this, i);
37659                 }
37660                 
37661                 return false;
37662             }
37663             
37664         }, this);
37665         
37666         this.validate();
37667     },
37668     
37669     clearInvalid : function(){
37670         
37671         if(!this.el || this.preventMark){
37672             return;
37673         }
37674         
37675         this.el.removeClass([this.invalidClass]);
37676         
37677         this.fireEvent('valid', this);
37678     }
37679     
37680 });
37681
37682 Roo.apply(Roo.bootstrap.RadioSet, {
37683     
37684     groups: {},
37685     
37686     register : function(set)
37687     {
37688         this.groups[set.name] = set;
37689     },
37690     
37691     get: function(name) 
37692     {
37693         if (typeof(this.groups[name]) == 'undefined') {
37694             return false;
37695         }
37696         
37697         return this.groups[name] ;
37698     }
37699     
37700 });
37701 /*
37702  * Based on:
37703  * Ext JS Library 1.1.1
37704  * Copyright(c) 2006-2007, Ext JS, LLC.
37705  *
37706  * Originally Released Under LGPL - original licence link has changed is not relivant.
37707  *
37708  * Fork - LGPL
37709  * <script type="text/javascript">
37710  */
37711
37712
37713 /**
37714  * @class Roo.bootstrap.SplitBar
37715  * @extends Roo.util.Observable
37716  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37717  * <br><br>
37718  * Usage:
37719  * <pre><code>
37720 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37721                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37722 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37723 split.minSize = 100;
37724 split.maxSize = 600;
37725 split.animate = true;
37726 split.on('moved', splitterMoved);
37727 </code></pre>
37728  * @constructor
37729  * Create a new SplitBar
37730  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37731  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37732  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37733  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37734                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37735                         position of the SplitBar).
37736  */
37737 Roo.bootstrap.SplitBar = function(cfg){
37738     
37739     /** @private */
37740     
37741     //{
37742     //  dragElement : elm
37743     //  resizingElement: el,
37744         // optional..
37745     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37746     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37747         // existingProxy ???
37748     //}
37749     
37750     this.el = Roo.get(cfg.dragElement, true);
37751     this.el.dom.unselectable = "on";
37752     /** @private */
37753     this.resizingEl = Roo.get(cfg.resizingElement, true);
37754
37755     /**
37756      * @private
37757      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37758      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37759      * @type Number
37760      */
37761     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37762     
37763     /**
37764      * The minimum size of the resizing element. (Defaults to 0)
37765      * @type Number
37766      */
37767     this.minSize = 0;
37768     
37769     /**
37770      * The maximum size of the resizing element. (Defaults to 2000)
37771      * @type Number
37772      */
37773     this.maxSize = 2000;
37774     
37775     /**
37776      * Whether to animate the transition to the new size
37777      * @type Boolean
37778      */
37779     this.animate = false;
37780     
37781     /**
37782      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37783      * @type Boolean
37784      */
37785     this.useShim = false;
37786     
37787     /** @private */
37788     this.shim = null;
37789     
37790     if(!cfg.existingProxy){
37791         /** @private */
37792         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37793     }else{
37794         this.proxy = Roo.get(cfg.existingProxy).dom;
37795     }
37796     /** @private */
37797     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37798     
37799     /** @private */
37800     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37801     
37802     /** @private */
37803     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37804     
37805     /** @private */
37806     this.dragSpecs = {};
37807     
37808     /**
37809      * @private The adapter to use to positon and resize elements
37810      */
37811     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37812     this.adapter.init(this);
37813     
37814     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37815         /** @private */
37816         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37817         this.el.addClass("roo-splitbar-h");
37818     }else{
37819         /** @private */
37820         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37821         this.el.addClass("roo-splitbar-v");
37822     }
37823     
37824     this.addEvents({
37825         /**
37826          * @event resize
37827          * Fires when the splitter is moved (alias for {@link #event-moved})
37828          * @param {Roo.bootstrap.SplitBar} this
37829          * @param {Number} newSize the new width or height
37830          */
37831         "resize" : true,
37832         /**
37833          * @event moved
37834          * Fires when the splitter is moved
37835          * @param {Roo.bootstrap.SplitBar} this
37836          * @param {Number} newSize the new width or height
37837          */
37838         "moved" : true,
37839         /**
37840          * @event beforeresize
37841          * Fires before the splitter is dragged
37842          * @param {Roo.bootstrap.SplitBar} this
37843          */
37844         "beforeresize" : true,
37845
37846         "beforeapply" : true
37847     });
37848
37849     Roo.util.Observable.call(this);
37850 };
37851
37852 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37853     onStartProxyDrag : function(x, y){
37854         this.fireEvent("beforeresize", this);
37855         if(!this.overlay){
37856             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37857             o.unselectable();
37858             o.enableDisplayMode("block");
37859             // all splitbars share the same overlay
37860             Roo.bootstrap.SplitBar.prototype.overlay = o;
37861         }
37862         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37863         this.overlay.show();
37864         Roo.get(this.proxy).setDisplayed("block");
37865         var size = this.adapter.getElementSize(this);
37866         this.activeMinSize = this.getMinimumSize();;
37867         this.activeMaxSize = this.getMaximumSize();;
37868         var c1 = size - this.activeMinSize;
37869         var c2 = Math.max(this.activeMaxSize - size, 0);
37870         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37871             this.dd.resetConstraints();
37872             this.dd.setXConstraint(
37873                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37874                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37875             );
37876             this.dd.setYConstraint(0, 0);
37877         }else{
37878             this.dd.resetConstraints();
37879             this.dd.setXConstraint(0, 0);
37880             this.dd.setYConstraint(
37881                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37882                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37883             );
37884          }
37885         this.dragSpecs.startSize = size;
37886         this.dragSpecs.startPoint = [x, y];
37887         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37888     },
37889     
37890     /** 
37891      * @private Called after the drag operation by the DDProxy
37892      */
37893     onEndProxyDrag : function(e){
37894         Roo.get(this.proxy).setDisplayed(false);
37895         var endPoint = Roo.lib.Event.getXY(e);
37896         if(this.overlay){
37897             this.overlay.hide();
37898         }
37899         var newSize;
37900         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37901             newSize = this.dragSpecs.startSize + 
37902                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37903                     endPoint[0] - this.dragSpecs.startPoint[0] :
37904                     this.dragSpecs.startPoint[0] - endPoint[0]
37905                 );
37906         }else{
37907             newSize = this.dragSpecs.startSize + 
37908                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37909                     endPoint[1] - this.dragSpecs.startPoint[1] :
37910                     this.dragSpecs.startPoint[1] - endPoint[1]
37911                 );
37912         }
37913         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37914         if(newSize != this.dragSpecs.startSize){
37915             if(this.fireEvent('beforeapply', this, newSize) !== false){
37916                 this.adapter.setElementSize(this, newSize);
37917                 this.fireEvent("moved", this, newSize);
37918                 this.fireEvent("resize", this, newSize);
37919             }
37920         }
37921     },
37922     
37923     /**
37924      * Get the adapter this SplitBar uses
37925      * @return The adapter object
37926      */
37927     getAdapter : function(){
37928         return this.adapter;
37929     },
37930     
37931     /**
37932      * Set the adapter this SplitBar uses
37933      * @param {Object} adapter A SplitBar adapter object
37934      */
37935     setAdapter : function(adapter){
37936         this.adapter = adapter;
37937         this.adapter.init(this);
37938     },
37939     
37940     /**
37941      * Gets the minimum size for the resizing element
37942      * @return {Number} The minimum size
37943      */
37944     getMinimumSize : function(){
37945         return this.minSize;
37946     },
37947     
37948     /**
37949      * Sets the minimum size for the resizing element
37950      * @param {Number} minSize The minimum size
37951      */
37952     setMinimumSize : function(minSize){
37953         this.minSize = minSize;
37954     },
37955     
37956     /**
37957      * Gets the maximum size for the resizing element
37958      * @return {Number} The maximum size
37959      */
37960     getMaximumSize : function(){
37961         return this.maxSize;
37962     },
37963     
37964     /**
37965      * Sets the maximum size for the resizing element
37966      * @param {Number} maxSize The maximum size
37967      */
37968     setMaximumSize : function(maxSize){
37969         this.maxSize = maxSize;
37970     },
37971     
37972     /**
37973      * Sets the initialize size for the resizing element
37974      * @param {Number} size The initial size
37975      */
37976     setCurrentSize : function(size){
37977         var oldAnimate = this.animate;
37978         this.animate = false;
37979         this.adapter.setElementSize(this, size);
37980         this.animate = oldAnimate;
37981     },
37982     
37983     /**
37984      * Destroy this splitbar. 
37985      * @param {Boolean} removeEl True to remove the element
37986      */
37987     destroy : function(removeEl){
37988         if(this.shim){
37989             this.shim.remove();
37990         }
37991         this.dd.unreg();
37992         this.proxy.parentNode.removeChild(this.proxy);
37993         if(removeEl){
37994             this.el.remove();
37995         }
37996     }
37997 });
37998
37999 /**
38000  * @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.
38001  */
38002 Roo.bootstrap.SplitBar.createProxy = function(dir){
38003     var proxy = new Roo.Element(document.createElement("div"));
38004     proxy.unselectable();
38005     var cls = 'roo-splitbar-proxy';
38006     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38007     document.body.appendChild(proxy.dom);
38008     return proxy.dom;
38009 };
38010
38011 /** 
38012  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38013  * Default Adapter. It assumes the splitter and resizing element are not positioned
38014  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38015  */
38016 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38017 };
38018
38019 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38020     // do nothing for now
38021     init : function(s){
38022     
38023     },
38024     /**
38025      * Called before drag operations to get the current size of the resizing element. 
38026      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38027      */
38028      getElementSize : function(s){
38029         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38030             return s.resizingEl.getWidth();
38031         }else{
38032             return s.resizingEl.getHeight();
38033         }
38034     },
38035     
38036     /**
38037      * Called after drag operations to set the size of the resizing element.
38038      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38039      * @param {Number} newSize The new size to set
38040      * @param {Function} onComplete A function to be invoked when resizing is complete
38041      */
38042     setElementSize : function(s, newSize, onComplete){
38043         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38044             if(!s.animate){
38045                 s.resizingEl.setWidth(newSize);
38046                 if(onComplete){
38047                     onComplete(s, newSize);
38048                 }
38049             }else{
38050                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38051             }
38052         }else{
38053             
38054             if(!s.animate){
38055                 s.resizingEl.setHeight(newSize);
38056                 if(onComplete){
38057                     onComplete(s, newSize);
38058                 }
38059             }else{
38060                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38061             }
38062         }
38063     }
38064 };
38065
38066 /** 
38067  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38068  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38069  * Adapter that  moves the splitter element to align with the resized sizing element. 
38070  * Used with an absolute positioned SplitBar.
38071  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38072  * document.body, make sure you assign an id to the body element.
38073  */
38074 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38075     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38076     this.container = Roo.get(container);
38077 };
38078
38079 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38080     init : function(s){
38081         this.basic.init(s);
38082     },
38083     
38084     getElementSize : function(s){
38085         return this.basic.getElementSize(s);
38086     },
38087     
38088     setElementSize : function(s, newSize, onComplete){
38089         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38090     },
38091     
38092     moveSplitter : function(s){
38093         var yes = Roo.bootstrap.SplitBar;
38094         switch(s.placement){
38095             case yes.LEFT:
38096                 s.el.setX(s.resizingEl.getRight());
38097                 break;
38098             case yes.RIGHT:
38099                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38100                 break;
38101             case yes.TOP:
38102                 s.el.setY(s.resizingEl.getBottom());
38103                 break;
38104             case yes.BOTTOM:
38105                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38106                 break;
38107         }
38108     }
38109 };
38110
38111 /**
38112  * Orientation constant - Create a vertical SplitBar
38113  * @static
38114  * @type Number
38115  */
38116 Roo.bootstrap.SplitBar.VERTICAL = 1;
38117
38118 /**
38119  * Orientation constant - Create a horizontal SplitBar
38120  * @static
38121  * @type Number
38122  */
38123 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38124
38125 /**
38126  * Placement constant - The resizing element is to the left of the splitter element
38127  * @static
38128  * @type Number
38129  */
38130 Roo.bootstrap.SplitBar.LEFT = 1;
38131
38132 /**
38133  * Placement constant - The resizing element is to the right of the splitter element
38134  * @static
38135  * @type Number
38136  */
38137 Roo.bootstrap.SplitBar.RIGHT = 2;
38138
38139 /**
38140  * Placement constant - The resizing element is positioned above the splitter element
38141  * @static
38142  * @type Number
38143  */
38144 Roo.bootstrap.SplitBar.TOP = 3;
38145
38146 /**
38147  * Placement constant - The resizing element is positioned under splitter element
38148  * @static
38149  * @type Number
38150  */
38151 Roo.bootstrap.SplitBar.BOTTOM = 4;
38152 Roo.namespace("Roo.bootstrap.layout");/*
38153  * Based on:
38154  * Ext JS Library 1.1.1
38155  * Copyright(c) 2006-2007, Ext JS, LLC.
38156  *
38157  * Originally Released Under LGPL - original licence link has changed is not relivant.
38158  *
38159  * Fork - LGPL
38160  * <script type="text/javascript">
38161  */
38162
38163 /**
38164  * @class Roo.bootstrap.layout.Manager
38165  * @extends Roo.bootstrap.Component
38166  * Base class for layout managers.
38167  */
38168 Roo.bootstrap.layout.Manager = function(config)
38169 {
38170     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38171
38172
38173
38174
38175
38176     /** false to disable window resize monitoring @type Boolean */
38177     this.monitorWindowResize = true;
38178     this.regions = {};
38179     this.addEvents({
38180         /**
38181          * @event layout
38182          * Fires when a layout is performed.
38183          * @param {Roo.LayoutManager} this
38184          */
38185         "layout" : true,
38186         /**
38187          * @event regionresized
38188          * Fires when the user resizes a region.
38189          * @param {Roo.LayoutRegion} region The resized region
38190          * @param {Number} newSize The new size (width for east/west, height for north/south)
38191          */
38192         "regionresized" : true,
38193         /**
38194          * @event regioncollapsed
38195          * Fires when a region is collapsed.
38196          * @param {Roo.LayoutRegion} region The collapsed region
38197          */
38198         "regioncollapsed" : true,
38199         /**
38200          * @event regionexpanded
38201          * Fires when a region is expanded.
38202          * @param {Roo.LayoutRegion} region The expanded region
38203          */
38204         "regionexpanded" : true
38205     });
38206     this.updating = false;
38207
38208     if (config.el) {
38209         this.el = Roo.get(config.el);
38210         this.initEvents();
38211     }
38212
38213 };
38214
38215 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38216
38217
38218     regions : null,
38219
38220     monitorWindowResize : true,
38221
38222
38223     updating : false,
38224
38225
38226     onRender : function(ct, position)
38227     {
38228         if(!this.el){
38229             this.el = Roo.get(ct);
38230             this.initEvents();
38231         }
38232         //this.fireEvent('render',this);
38233     },
38234
38235
38236     initEvents: function()
38237     {
38238
38239
38240         // ie scrollbar fix
38241         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38242             document.body.scroll = "no";
38243         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38244             this.el.position('relative');
38245         }
38246         this.id = this.el.id;
38247         this.el.addClass("roo-layout-container");
38248         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38249         if(this.el.dom != document.body ) {
38250             this.el.on('resize', this.layout,this);
38251             this.el.on('show', this.layout,this);
38252         }
38253
38254     },
38255
38256     /**
38257      * Returns true if this layout is currently being updated
38258      * @return {Boolean}
38259      */
38260     isUpdating : function(){
38261         return this.updating;
38262     },
38263
38264     /**
38265      * Suspend the LayoutManager from doing auto-layouts while
38266      * making multiple add or remove calls
38267      */
38268     beginUpdate : function(){
38269         this.updating = true;
38270     },
38271
38272     /**
38273      * Restore auto-layouts and optionally disable the manager from performing a layout
38274      * @param {Boolean} noLayout true to disable a layout update
38275      */
38276     endUpdate : function(noLayout){
38277         this.updating = false;
38278         if(!noLayout){
38279             this.layout();
38280         }
38281     },
38282
38283     layout: function(){
38284         // abstract...
38285     },
38286
38287     onRegionResized : function(region, newSize){
38288         this.fireEvent("regionresized", region, newSize);
38289         this.layout();
38290     },
38291
38292     onRegionCollapsed : function(region){
38293         this.fireEvent("regioncollapsed", region);
38294     },
38295
38296     onRegionExpanded : function(region){
38297         this.fireEvent("regionexpanded", region);
38298     },
38299
38300     /**
38301      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38302      * performs box-model adjustments.
38303      * @return {Object} The size as an object {width: (the width), height: (the height)}
38304      */
38305     getViewSize : function()
38306     {
38307         var size;
38308         if(this.el.dom != document.body){
38309             size = this.el.getSize();
38310         }else{
38311             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38312         }
38313         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38314         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38315         return size;
38316     },
38317
38318     /**
38319      * Returns the Element this layout is bound to.
38320      * @return {Roo.Element}
38321      */
38322     getEl : function(){
38323         return this.el;
38324     },
38325
38326     /**
38327      * Returns the specified region.
38328      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38329      * @return {Roo.LayoutRegion}
38330      */
38331     getRegion : function(target){
38332         return this.regions[target.toLowerCase()];
38333     },
38334
38335     onWindowResize : function(){
38336         if(this.monitorWindowResize){
38337             this.layout();
38338         }
38339     }
38340 });
38341 /*
38342  * Based on:
38343  * Ext JS Library 1.1.1
38344  * Copyright(c) 2006-2007, Ext JS, LLC.
38345  *
38346  * Originally Released Under LGPL - original licence link has changed is not relivant.
38347  *
38348  * Fork - LGPL
38349  * <script type="text/javascript">
38350  */
38351 /**
38352  * @class Roo.bootstrap.layout.Border
38353  * @extends Roo.bootstrap.layout.Manager
38354  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38355  * please see: examples/bootstrap/nested.html<br><br>
38356  
38357 <b>The container the layout is rendered into can be either the body element or any other element.
38358 If it is not the body element, the container needs to either be an absolute positioned element,
38359 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38360 the container size if it is not the body element.</b>
38361
38362 * @constructor
38363 * Create a new Border
38364 * @param {Object} config Configuration options
38365  */
38366 Roo.bootstrap.layout.Border = function(config){
38367     config = config || {};
38368     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38369     
38370     
38371     
38372     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38373         if(config[region]){
38374             config[region].region = region;
38375             this.addRegion(config[region]);
38376         }
38377     },this);
38378     
38379 };
38380
38381 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38382
38383 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38384     
38385     parent : false, // this might point to a 'nest' or a ???
38386     
38387     /**
38388      * Creates and adds a new region if it doesn't already exist.
38389      * @param {String} target The target region key (north, south, east, west or center).
38390      * @param {Object} config The regions config object
38391      * @return {BorderLayoutRegion} The new region
38392      */
38393     addRegion : function(config)
38394     {
38395         if(!this.regions[config.region]){
38396             var r = this.factory(config);
38397             this.bindRegion(r);
38398         }
38399         return this.regions[config.region];
38400     },
38401
38402     // private (kinda)
38403     bindRegion : function(r){
38404         this.regions[r.config.region] = r;
38405         
38406         r.on("visibilitychange",    this.layout, this);
38407         r.on("paneladded",          this.layout, this);
38408         r.on("panelremoved",        this.layout, this);
38409         r.on("invalidated",         this.layout, this);
38410         r.on("resized",             this.onRegionResized, this);
38411         r.on("collapsed",           this.onRegionCollapsed, this);
38412         r.on("expanded",            this.onRegionExpanded, this);
38413     },
38414
38415     /**
38416      * Performs a layout update.
38417      */
38418     layout : function()
38419     {
38420         if(this.updating) {
38421             return;
38422         }
38423         
38424         // render all the rebions if they have not been done alreayd?
38425         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38426             if(this.regions[region] && !this.regions[region].bodyEl){
38427                 this.regions[region].onRender(this.el)
38428             }
38429         },this);
38430         
38431         var size = this.getViewSize();
38432         var w = size.width;
38433         var h = size.height;
38434         var centerW = w;
38435         var centerH = h;
38436         var centerY = 0;
38437         var centerX = 0;
38438         //var x = 0, y = 0;
38439
38440         var rs = this.regions;
38441         var north = rs["north"];
38442         var south = rs["south"]; 
38443         var west = rs["west"];
38444         var east = rs["east"];
38445         var center = rs["center"];
38446         //if(this.hideOnLayout){ // not supported anymore
38447             //c.el.setStyle("display", "none");
38448         //}
38449         if(north && north.isVisible()){
38450             var b = north.getBox();
38451             var m = north.getMargins();
38452             b.width = w - (m.left+m.right);
38453             b.x = m.left;
38454             b.y = m.top;
38455             centerY = b.height + b.y + m.bottom;
38456             centerH -= centerY;
38457             north.updateBox(this.safeBox(b));
38458         }
38459         if(south && south.isVisible()){
38460             var b = south.getBox();
38461             var m = south.getMargins();
38462             b.width = w - (m.left+m.right);
38463             b.x = m.left;
38464             var totalHeight = (b.height + m.top + m.bottom);
38465             b.y = h - totalHeight + m.top;
38466             centerH -= totalHeight;
38467             south.updateBox(this.safeBox(b));
38468         }
38469         if(west && west.isVisible()){
38470             var b = west.getBox();
38471             var m = west.getMargins();
38472             b.height = centerH - (m.top+m.bottom);
38473             b.x = m.left;
38474             b.y = centerY + m.top;
38475             var totalWidth = (b.width + m.left + m.right);
38476             centerX += totalWidth;
38477             centerW -= totalWidth;
38478             west.updateBox(this.safeBox(b));
38479         }
38480         if(east && east.isVisible()){
38481             var b = east.getBox();
38482             var m = east.getMargins();
38483             b.height = centerH - (m.top+m.bottom);
38484             var totalWidth = (b.width + m.left + m.right);
38485             b.x = w - totalWidth + m.left;
38486             b.y = centerY + m.top;
38487             centerW -= totalWidth;
38488             east.updateBox(this.safeBox(b));
38489         }
38490         if(center){
38491             var m = center.getMargins();
38492             var centerBox = {
38493                 x: centerX + m.left,
38494                 y: centerY + m.top,
38495                 width: centerW - (m.left+m.right),
38496                 height: centerH - (m.top+m.bottom)
38497             };
38498             //if(this.hideOnLayout){
38499                 //center.el.setStyle("display", "block");
38500             //}
38501             center.updateBox(this.safeBox(centerBox));
38502         }
38503         this.el.repaint();
38504         this.fireEvent("layout", this);
38505     },
38506
38507     // private
38508     safeBox : function(box){
38509         box.width = Math.max(0, box.width);
38510         box.height = Math.max(0, box.height);
38511         return box;
38512     },
38513
38514     /**
38515      * Adds a ContentPanel (or subclass) to this layout.
38516      * @param {String} target The target region key (north, south, east, west or center).
38517      * @param {Roo.ContentPanel} panel The panel to add
38518      * @return {Roo.ContentPanel} The added panel
38519      */
38520     add : function(target, panel){
38521          
38522         target = target.toLowerCase();
38523         return this.regions[target].add(panel);
38524     },
38525
38526     /**
38527      * Remove a ContentPanel (or subclass) to this layout.
38528      * @param {String} target The target region key (north, south, east, west or center).
38529      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38530      * @return {Roo.ContentPanel} The removed panel
38531      */
38532     remove : function(target, panel){
38533         target = target.toLowerCase();
38534         return this.regions[target].remove(panel);
38535     },
38536
38537     /**
38538      * Searches all regions for a panel with the specified id
38539      * @param {String} panelId
38540      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38541      */
38542     findPanel : function(panelId){
38543         var rs = this.regions;
38544         for(var target in rs){
38545             if(typeof rs[target] != "function"){
38546                 var p = rs[target].getPanel(panelId);
38547                 if(p){
38548                     return p;
38549                 }
38550             }
38551         }
38552         return null;
38553     },
38554
38555     /**
38556      * Searches all regions for a panel with the specified id and activates (shows) it.
38557      * @param {String/ContentPanel} panelId The panels id or the panel itself
38558      * @return {Roo.ContentPanel} The shown panel or null
38559      */
38560     showPanel : function(panelId) {
38561       var rs = this.regions;
38562       for(var target in rs){
38563          var r = rs[target];
38564          if(typeof r != "function"){
38565             if(r.hasPanel(panelId)){
38566                return r.showPanel(panelId);
38567             }
38568          }
38569       }
38570       return null;
38571    },
38572
38573    /**
38574      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38575      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38576      */
38577    /*
38578     restoreState : function(provider){
38579         if(!provider){
38580             provider = Roo.state.Manager;
38581         }
38582         var sm = new Roo.LayoutStateManager();
38583         sm.init(this, provider);
38584     },
38585 */
38586  
38587  
38588     /**
38589      * Adds a xtype elements to the layout.
38590      * <pre><code>
38591
38592 layout.addxtype({
38593        xtype : 'ContentPanel',
38594        region: 'west',
38595        items: [ .... ]
38596    }
38597 );
38598
38599 layout.addxtype({
38600         xtype : 'NestedLayoutPanel',
38601         region: 'west',
38602         layout: {
38603            center: { },
38604            west: { }   
38605         },
38606         items : [ ... list of content panels or nested layout panels.. ]
38607    }
38608 );
38609 </code></pre>
38610      * @param {Object} cfg Xtype definition of item to add.
38611      */
38612     addxtype : function(cfg)
38613     {
38614         // basically accepts a pannel...
38615         // can accept a layout region..!?!?
38616         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38617         
38618         
38619         // theory?  children can only be panels??
38620         
38621         //if (!cfg.xtype.match(/Panel$/)) {
38622         //    return false;
38623         //}
38624         var ret = false;
38625         
38626         if (typeof(cfg.region) == 'undefined') {
38627             Roo.log("Failed to add Panel, region was not set");
38628             Roo.log(cfg);
38629             return false;
38630         }
38631         var region = cfg.region;
38632         delete cfg.region;
38633         
38634           
38635         var xitems = [];
38636         if (cfg.items) {
38637             xitems = cfg.items;
38638             delete cfg.items;
38639         }
38640         var nb = false;
38641         
38642         if ( region == 'center') {
38643             Roo.log("Center: " + cfg.title);
38644         }
38645         
38646         
38647         switch(cfg.xtype) 
38648         {
38649             case 'Content':  // ContentPanel (el, cfg)
38650             case 'Scroll':  // ContentPanel (el, cfg)
38651             case 'View': 
38652                 cfg.autoCreate = cfg.autoCreate || true;
38653                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38654                 //} else {
38655                 //    var el = this.el.createChild();
38656                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38657                 //}
38658                 
38659                 this.add(region, ret);
38660                 break;
38661             
38662             /*
38663             case 'TreePanel': // our new panel!
38664                 cfg.el = this.el.createChild();
38665                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38666                 this.add(region, ret);
38667                 break;
38668             */
38669             
38670             case 'Nest': 
38671                 // create a new Layout (which is  a Border Layout...
38672                 
38673                 var clayout = cfg.layout;
38674                 clayout.el  = this.el.createChild();
38675                 clayout.items   = clayout.items  || [];
38676                 
38677                 delete cfg.layout;
38678                 
38679                 // replace this exitems with the clayout ones..
38680                 xitems = clayout.items;
38681                  
38682                 // force background off if it's in center...
38683                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38684                     cfg.background = false;
38685                 }
38686                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38687                 
38688                 
38689                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38690                 //console.log('adding nested layout panel '  + cfg.toSource());
38691                 this.add(region, ret);
38692                 nb = {}; /// find first...
38693                 break;
38694             
38695             case 'Grid':
38696                 
38697                 // needs grid and region
38698                 
38699                 //var el = this.getRegion(region).el.createChild();
38700                 /*
38701                  *var el = this.el.createChild();
38702                 // create the grid first...
38703                 cfg.grid.container = el;
38704                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38705                 */
38706                 
38707                 if (region == 'center' && this.active ) {
38708                     cfg.background = false;
38709                 }
38710                 
38711                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38712                 
38713                 this.add(region, ret);
38714                 /*
38715                 if (cfg.background) {
38716                     // render grid on panel activation (if panel background)
38717                     ret.on('activate', function(gp) {
38718                         if (!gp.grid.rendered) {
38719                     //        gp.grid.render(el);
38720                         }
38721                     });
38722                 } else {
38723                   //  cfg.grid.render(el);
38724                 }
38725                 */
38726                 break;
38727            
38728            
38729             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38730                 // it was the old xcomponent building that caused this before.
38731                 // espeically if border is the top element in the tree.
38732                 ret = this;
38733                 break; 
38734                 
38735                     
38736                 
38737                 
38738                 
38739             default:
38740                 /*
38741                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38742                     
38743                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38744                     this.add(region, ret);
38745                 } else {
38746                 */
38747                     Roo.log(cfg);
38748                     throw "Can not add '" + cfg.xtype + "' to Border";
38749                     return null;
38750              
38751                                 
38752              
38753         }
38754         this.beginUpdate();
38755         // add children..
38756         var region = '';
38757         var abn = {};
38758         Roo.each(xitems, function(i)  {
38759             region = nb && i.region ? i.region : false;
38760             
38761             var add = ret.addxtype(i);
38762            
38763             if (region) {
38764                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38765                 if (!i.background) {
38766                     abn[region] = nb[region] ;
38767                 }
38768             }
38769             
38770         });
38771         this.endUpdate();
38772
38773         // make the last non-background panel active..
38774         //if (nb) { Roo.log(abn); }
38775         if (nb) {
38776             
38777             for(var r in abn) {
38778                 region = this.getRegion(r);
38779                 if (region) {
38780                     // tried using nb[r], but it does not work..
38781                      
38782                     region.showPanel(abn[r]);
38783                    
38784                 }
38785             }
38786         }
38787         return ret;
38788         
38789     },
38790     
38791     
38792 // private
38793     factory : function(cfg)
38794     {
38795         
38796         var validRegions = Roo.bootstrap.layout.Border.regions;
38797
38798         var target = cfg.region;
38799         cfg.mgr = this;
38800         
38801         var r = Roo.bootstrap.layout;
38802         Roo.log(target);
38803         switch(target){
38804             case "north":
38805                 return new r.North(cfg);
38806             case "south":
38807                 return new r.South(cfg);
38808             case "east":
38809                 return new r.East(cfg);
38810             case "west":
38811                 return new r.West(cfg);
38812             case "center":
38813                 return new r.Center(cfg);
38814         }
38815         throw 'Layout region "'+target+'" not supported.';
38816     }
38817     
38818     
38819 });
38820  /*
38821  * Based on:
38822  * Ext JS Library 1.1.1
38823  * Copyright(c) 2006-2007, Ext JS, LLC.
38824  *
38825  * Originally Released Under LGPL - original licence link has changed is not relivant.
38826  *
38827  * Fork - LGPL
38828  * <script type="text/javascript">
38829  */
38830  
38831 /**
38832  * @class Roo.bootstrap.layout.Basic
38833  * @extends Roo.util.Observable
38834  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38835  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38836  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38837  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38838  * @cfg {string}   region  the region that it inhabits..
38839  * @cfg {bool}   skipConfig skip config?
38840  * 
38841
38842  */
38843 Roo.bootstrap.layout.Basic = function(config){
38844     
38845     this.mgr = config.mgr;
38846     
38847     this.position = config.region;
38848     
38849     var skipConfig = config.skipConfig;
38850     
38851     this.events = {
38852         /**
38853          * @scope Roo.BasicLayoutRegion
38854          */
38855         
38856         /**
38857          * @event beforeremove
38858          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38859          * @param {Roo.LayoutRegion} this
38860          * @param {Roo.ContentPanel} panel The panel
38861          * @param {Object} e The cancel event object
38862          */
38863         "beforeremove" : true,
38864         /**
38865          * @event invalidated
38866          * Fires when the layout for this region is changed.
38867          * @param {Roo.LayoutRegion} this
38868          */
38869         "invalidated" : true,
38870         /**
38871          * @event visibilitychange
38872          * Fires when this region is shown or hidden 
38873          * @param {Roo.LayoutRegion} this
38874          * @param {Boolean} visibility true or false
38875          */
38876         "visibilitychange" : true,
38877         /**
38878          * @event paneladded
38879          * Fires when a panel is added. 
38880          * @param {Roo.LayoutRegion} this
38881          * @param {Roo.ContentPanel} panel The panel
38882          */
38883         "paneladded" : true,
38884         /**
38885          * @event panelremoved
38886          * Fires when a panel is removed. 
38887          * @param {Roo.LayoutRegion} this
38888          * @param {Roo.ContentPanel} panel The panel
38889          */
38890         "panelremoved" : true,
38891         /**
38892          * @event beforecollapse
38893          * Fires when this region before collapse.
38894          * @param {Roo.LayoutRegion} this
38895          */
38896         "beforecollapse" : true,
38897         /**
38898          * @event collapsed
38899          * Fires when this region is collapsed.
38900          * @param {Roo.LayoutRegion} this
38901          */
38902         "collapsed" : true,
38903         /**
38904          * @event expanded
38905          * Fires when this region is expanded.
38906          * @param {Roo.LayoutRegion} this
38907          */
38908         "expanded" : true,
38909         /**
38910          * @event slideshow
38911          * Fires when this region is slid into view.
38912          * @param {Roo.LayoutRegion} this
38913          */
38914         "slideshow" : true,
38915         /**
38916          * @event slidehide
38917          * Fires when this region slides out of view. 
38918          * @param {Roo.LayoutRegion} this
38919          */
38920         "slidehide" : true,
38921         /**
38922          * @event panelactivated
38923          * Fires when a panel is activated. 
38924          * @param {Roo.LayoutRegion} this
38925          * @param {Roo.ContentPanel} panel The activated panel
38926          */
38927         "panelactivated" : true,
38928         /**
38929          * @event resized
38930          * Fires when the user resizes this region. 
38931          * @param {Roo.LayoutRegion} this
38932          * @param {Number} newSize The new size (width for east/west, height for north/south)
38933          */
38934         "resized" : true
38935     };
38936     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38937     this.panels = new Roo.util.MixedCollection();
38938     this.panels.getKey = this.getPanelId.createDelegate(this);
38939     this.box = null;
38940     this.activePanel = null;
38941     // ensure listeners are added...
38942     
38943     if (config.listeners || config.events) {
38944         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38945             listeners : config.listeners || {},
38946             events : config.events || {}
38947         });
38948     }
38949     
38950     if(skipConfig !== true){
38951         this.applyConfig(config);
38952     }
38953 };
38954
38955 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38956 {
38957     getPanelId : function(p){
38958         return p.getId();
38959     },
38960     
38961     applyConfig : function(config){
38962         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38963         this.config = config;
38964         
38965     },
38966     
38967     /**
38968      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38969      * the width, for horizontal (north, south) the height.
38970      * @param {Number} newSize The new width or height
38971      */
38972     resizeTo : function(newSize){
38973         var el = this.el ? this.el :
38974                  (this.activePanel ? this.activePanel.getEl() : null);
38975         if(el){
38976             switch(this.position){
38977                 case "east":
38978                 case "west":
38979                     el.setWidth(newSize);
38980                     this.fireEvent("resized", this, newSize);
38981                 break;
38982                 case "north":
38983                 case "south":
38984                     el.setHeight(newSize);
38985                     this.fireEvent("resized", this, newSize);
38986                 break;                
38987             }
38988         }
38989     },
38990     
38991     getBox : function(){
38992         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38993     },
38994     
38995     getMargins : function(){
38996         return this.margins;
38997     },
38998     
38999     updateBox : function(box){
39000         this.box = box;
39001         var el = this.activePanel.getEl();
39002         el.dom.style.left = box.x + "px";
39003         el.dom.style.top = box.y + "px";
39004         this.activePanel.setSize(box.width, box.height);
39005     },
39006     
39007     /**
39008      * Returns the container element for this region.
39009      * @return {Roo.Element}
39010      */
39011     getEl : function(){
39012         return this.activePanel;
39013     },
39014     
39015     /**
39016      * Returns true if this region is currently visible.
39017      * @return {Boolean}
39018      */
39019     isVisible : function(){
39020         return this.activePanel ? true : false;
39021     },
39022     
39023     setActivePanel : function(panel){
39024         panel = this.getPanel(panel);
39025         if(this.activePanel && this.activePanel != panel){
39026             this.activePanel.setActiveState(false);
39027             this.activePanel.getEl().setLeftTop(-10000,-10000);
39028         }
39029         this.activePanel = panel;
39030         panel.setActiveState(true);
39031         if(this.box){
39032             panel.setSize(this.box.width, this.box.height);
39033         }
39034         this.fireEvent("panelactivated", this, panel);
39035         this.fireEvent("invalidated");
39036     },
39037     
39038     /**
39039      * Show the specified panel.
39040      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39041      * @return {Roo.ContentPanel} The shown panel or null
39042      */
39043     showPanel : function(panel){
39044         panel = this.getPanel(panel);
39045         if(panel){
39046             this.setActivePanel(panel);
39047         }
39048         return panel;
39049     },
39050     
39051     /**
39052      * Get the active panel for this region.
39053      * @return {Roo.ContentPanel} The active panel or null
39054      */
39055     getActivePanel : function(){
39056         return this.activePanel;
39057     },
39058     
39059     /**
39060      * Add the passed ContentPanel(s)
39061      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39062      * @return {Roo.ContentPanel} The panel added (if only one was added)
39063      */
39064     add : function(panel){
39065         if(arguments.length > 1){
39066             for(var i = 0, len = arguments.length; i < len; i++) {
39067                 this.add(arguments[i]);
39068             }
39069             return null;
39070         }
39071         if(this.hasPanel(panel)){
39072             this.showPanel(panel);
39073             return panel;
39074         }
39075         var el = panel.getEl();
39076         if(el.dom.parentNode != this.mgr.el.dom){
39077             this.mgr.el.dom.appendChild(el.dom);
39078         }
39079         if(panel.setRegion){
39080             panel.setRegion(this);
39081         }
39082         this.panels.add(panel);
39083         el.setStyle("position", "absolute");
39084         if(!panel.background){
39085             this.setActivePanel(panel);
39086             if(this.config.initialSize && this.panels.getCount()==1){
39087                 this.resizeTo(this.config.initialSize);
39088             }
39089         }
39090         this.fireEvent("paneladded", this, panel);
39091         return panel;
39092     },
39093     
39094     /**
39095      * Returns true if the panel is in this region.
39096      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39097      * @return {Boolean}
39098      */
39099     hasPanel : function(panel){
39100         if(typeof panel == "object"){ // must be panel obj
39101             panel = panel.getId();
39102         }
39103         return this.getPanel(panel) ? true : false;
39104     },
39105     
39106     /**
39107      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39108      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39109      * @param {Boolean} preservePanel Overrides the config preservePanel option
39110      * @return {Roo.ContentPanel} The panel that was removed
39111      */
39112     remove : function(panel, preservePanel){
39113         panel = this.getPanel(panel);
39114         if(!panel){
39115             return null;
39116         }
39117         var e = {};
39118         this.fireEvent("beforeremove", this, panel, e);
39119         if(e.cancel === true){
39120             return null;
39121         }
39122         var panelId = panel.getId();
39123         this.panels.removeKey(panelId);
39124         return panel;
39125     },
39126     
39127     /**
39128      * Returns the panel specified or null if it's not in this region.
39129      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39130      * @return {Roo.ContentPanel}
39131      */
39132     getPanel : function(id){
39133         if(typeof id == "object"){ // must be panel obj
39134             return id;
39135         }
39136         return this.panels.get(id);
39137     },
39138     
39139     /**
39140      * Returns this regions position (north/south/east/west/center).
39141      * @return {String} 
39142      */
39143     getPosition: function(){
39144         return this.position;    
39145     }
39146 });/*
39147  * Based on:
39148  * Ext JS Library 1.1.1
39149  * Copyright(c) 2006-2007, Ext JS, LLC.
39150  *
39151  * Originally Released Under LGPL - original licence link has changed is not relivant.
39152  *
39153  * Fork - LGPL
39154  * <script type="text/javascript">
39155  */
39156  
39157 /**
39158  * @class Roo.bootstrap.layout.Region
39159  * @extends Roo.bootstrap.layout.Basic
39160  * This class represents a region in a layout manager.
39161  
39162  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39163  * @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})
39164  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39165  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39166  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39167  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39168  * @cfg {String}    title           The title for the region (overrides panel titles)
39169  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39170  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39171  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39172  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39173  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39174  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39175  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39176  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39177  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39178  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39179
39180  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39181  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39182  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39183  * @cfg {Number}    width           For East/West panels
39184  * @cfg {Number}    height          For North/South panels
39185  * @cfg {Boolean}   split           To show the splitter
39186  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39187  * 
39188  * @cfg {string}   cls             Extra CSS classes to add to region
39189  * 
39190  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39191  * @cfg {string}   region  the region that it inhabits..
39192  *
39193
39194  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39195  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39196
39197  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39198  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39199  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39200  */
39201 Roo.bootstrap.layout.Region = function(config)
39202 {
39203     this.applyConfig(config);
39204
39205     var mgr = config.mgr;
39206     var pos = config.region;
39207     config.skipConfig = true;
39208     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39209     
39210     if (mgr.el) {
39211         this.onRender(mgr.el);   
39212     }
39213      
39214     this.visible = true;
39215     this.collapsed = false;
39216     this.unrendered_panels = [];
39217 };
39218
39219 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39220
39221     position: '', // set by wrapper (eg. north/south etc..)
39222     unrendered_panels : null,  // unrendered panels.
39223     
39224     tabPosition : false,
39225     
39226     mgr: false, // points to 'Border'
39227     
39228     
39229     createBody : function(){
39230         /** This region's body element 
39231         * @type Roo.Element */
39232         this.bodyEl = this.el.createChild({
39233                 tag: "div",
39234                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39235         });
39236     },
39237
39238     onRender: function(ctr, pos)
39239     {
39240         var dh = Roo.DomHelper;
39241         /** This region's container element 
39242         * @type Roo.Element */
39243         this.el = dh.append(ctr.dom, {
39244                 tag: "div",
39245                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39246             }, true);
39247         /** This region's title element 
39248         * @type Roo.Element */
39249     
39250         this.titleEl = dh.append(this.el.dom,  {
39251                 tag: "div",
39252                 unselectable: "on",
39253                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39254                 children:[
39255                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39256                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39257                 ]
39258             }, true);
39259         
39260         this.titleEl.enableDisplayMode();
39261         /** This region's title text element 
39262         * @type HTMLElement */
39263         this.titleTextEl = this.titleEl.dom.firstChild;
39264         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39265         /*
39266         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39267         this.closeBtn.enableDisplayMode();
39268         this.closeBtn.on("click", this.closeClicked, this);
39269         this.closeBtn.hide();
39270     */
39271         this.createBody(this.config);
39272         if(this.config.hideWhenEmpty){
39273             this.hide();
39274             this.on("paneladded", this.validateVisibility, this);
39275             this.on("panelremoved", this.validateVisibility, this);
39276         }
39277         if(this.autoScroll){
39278             this.bodyEl.setStyle("overflow", "auto");
39279         }else{
39280             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39281         }
39282         //if(c.titlebar !== false){
39283             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39284                 this.titleEl.hide();
39285             }else{
39286                 this.titleEl.show();
39287                 if(this.config.title){
39288                     this.titleTextEl.innerHTML = this.config.title;
39289                 }
39290             }
39291         //}
39292         if(this.config.collapsed){
39293             this.collapse(true);
39294         }
39295         if(this.config.hidden){
39296             this.hide();
39297         }
39298         
39299         if (this.unrendered_panels && this.unrendered_panels.length) {
39300             for (var i =0;i< this.unrendered_panels.length; i++) {
39301                 this.add(this.unrendered_panels[i]);
39302             }
39303             this.unrendered_panels = null;
39304             
39305         }
39306         
39307     },
39308     
39309     applyConfig : function(c)
39310     {
39311         /*
39312          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39313             var dh = Roo.DomHelper;
39314             if(c.titlebar !== false){
39315                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39316                 this.collapseBtn.on("click", this.collapse, this);
39317                 this.collapseBtn.enableDisplayMode();
39318                 /*
39319                 if(c.showPin === true || this.showPin){
39320                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39321                     this.stickBtn.enableDisplayMode();
39322                     this.stickBtn.on("click", this.expand, this);
39323                     this.stickBtn.hide();
39324                 }
39325                 
39326             }
39327             */
39328             /** This region's collapsed element
39329             * @type Roo.Element */
39330             /*
39331              *
39332             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39333                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39334             ]}, true);
39335             
39336             if(c.floatable !== false){
39337                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39338                this.collapsedEl.on("click", this.collapseClick, this);
39339             }
39340
39341             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39342                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39343                    id: "message", unselectable: "on", style:{"float":"left"}});
39344                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39345              }
39346             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39347             this.expandBtn.on("click", this.expand, this);
39348             
39349         }
39350         
39351         if(this.collapseBtn){
39352             this.collapseBtn.setVisible(c.collapsible == true);
39353         }
39354         
39355         this.cmargins = c.cmargins || this.cmargins ||
39356                          (this.position == "west" || this.position == "east" ?
39357                              {top: 0, left: 2, right:2, bottom: 0} :
39358                              {top: 2, left: 0, right:0, bottom: 2});
39359         */
39360         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39361         
39362         
39363         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39364         
39365         this.autoScroll = c.autoScroll || false;
39366         
39367         
39368        
39369         
39370         this.duration = c.duration || .30;
39371         this.slideDuration = c.slideDuration || .45;
39372         this.config = c;
39373        
39374     },
39375     /**
39376      * Returns true if this region is currently visible.
39377      * @return {Boolean}
39378      */
39379     isVisible : function(){
39380         return this.visible;
39381     },
39382
39383     /**
39384      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39385      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39386      */
39387     //setCollapsedTitle : function(title){
39388     //    title = title || "&#160;";
39389      //   if(this.collapsedTitleTextEl){
39390       //      this.collapsedTitleTextEl.innerHTML = title;
39391        // }
39392     //},
39393
39394     getBox : function(){
39395         var b;
39396       //  if(!this.collapsed){
39397             b = this.el.getBox(false, true);
39398        // }else{
39399           //  b = this.collapsedEl.getBox(false, true);
39400         //}
39401         return b;
39402     },
39403
39404     getMargins : function(){
39405         return this.margins;
39406         //return this.collapsed ? this.cmargins : this.margins;
39407     },
39408 /*
39409     highlight : function(){
39410         this.el.addClass("x-layout-panel-dragover");
39411     },
39412
39413     unhighlight : function(){
39414         this.el.removeClass("x-layout-panel-dragover");
39415     },
39416 */
39417     updateBox : function(box)
39418     {
39419         if (!this.bodyEl) {
39420             return; // not rendered yet..
39421         }
39422         
39423         this.box = box;
39424         if(!this.collapsed){
39425             this.el.dom.style.left = box.x + "px";
39426             this.el.dom.style.top = box.y + "px";
39427             this.updateBody(box.width, box.height);
39428         }else{
39429             this.collapsedEl.dom.style.left = box.x + "px";
39430             this.collapsedEl.dom.style.top = box.y + "px";
39431             this.collapsedEl.setSize(box.width, box.height);
39432         }
39433         if(this.tabs){
39434             this.tabs.autoSizeTabs();
39435         }
39436     },
39437
39438     updateBody : function(w, h)
39439     {
39440         if(w !== null){
39441             this.el.setWidth(w);
39442             w -= this.el.getBorderWidth("rl");
39443             if(this.config.adjustments){
39444                 w += this.config.adjustments[0];
39445             }
39446         }
39447         if(h !== null && h > 0){
39448             this.el.setHeight(h);
39449             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39450             h -= this.el.getBorderWidth("tb");
39451             if(this.config.adjustments){
39452                 h += this.config.adjustments[1];
39453             }
39454             this.bodyEl.setHeight(h);
39455             if(this.tabs){
39456                 h = this.tabs.syncHeight(h);
39457             }
39458         }
39459         if(this.panelSize){
39460             w = w !== null ? w : this.panelSize.width;
39461             h = h !== null ? h : this.panelSize.height;
39462         }
39463         if(this.activePanel){
39464             var el = this.activePanel.getEl();
39465             w = w !== null ? w : el.getWidth();
39466             h = h !== null ? h : el.getHeight();
39467             this.panelSize = {width: w, height: h};
39468             this.activePanel.setSize(w, h);
39469         }
39470         if(Roo.isIE && this.tabs){
39471             this.tabs.el.repaint();
39472         }
39473     },
39474
39475     /**
39476      * Returns the container element for this region.
39477      * @return {Roo.Element}
39478      */
39479     getEl : function(){
39480         return this.el;
39481     },
39482
39483     /**
39484      * Hides this region.
39485      */
39486     hide : function(){
39487         //if(!this.collapsed){
39488             this.el.dom.style.left = "-2000px";
39489             this.el.hide();
39490         //}else{
39491          //   this.collapsedEl.dom.style.left = "-2000px";
39492          //   this.collapsedEl.hide();
39493        // }
39494         this.visible = false;
39495         this.fireEvent("visibilitychange", this, false);
39496     },
39497
39498     /**
39499      * Shows this region if it was previously hidden.
39500      */
39501     show : function(){
39502         //if(!this.collapsed){
39503             this.el.show();
39504         //}else{
39505         //    this.collapsedEl.show();
39506        // }
39507         this.visible = true;
39508         this.fireEvent("visibilitychange", this, true);
39509     },
39510 /*
39511     closeClicked : function(){
39512         if(this.activePanel){
39513             this.remove(this.activePanel);
39514         }
39515     },
39516
39517     collapseClick : function(e){
39518         if(this.isSlid){
39519            e.stopPropagation();
39520            this.slideIn();
39521         }else{
39522            e.stopPropagation();
39523            this.slideOut();
39524         }
39525     },
39526 */
39527     /**
39528      * Collapses this region.
39529      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39530      */
39531     /*
39532     collapse : function(skipAnim, skipCheck = false){
39533         if(this.collapsed) {
39534             return;
39535         }
39536         
39537         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39538             
39539             this.collapsed = true;
39540             if(this.split){
39541                 this.split.el.hide();
39542             }
39543             if(this.config.animate && skipAnim !== true){
39544                 this.fireEvent("invalidated", this);
39545                 this.animateCollapse();
39546             }else{
39547                 this.el.setLocation(-20000,-20000);
39548                 this.el.hide();
39549                 this.collapsedEl.show();
39550                 this.fireEvent("collapsed", this);
39551                 this.fireEvent("invalidated", this);
39552             }
39553         }
39554         
39555     },
39556 */
39557     animateCollapse : function(){
39558         // overridden
39559     },
39560
39561     /**
39562      * Expands this region if it was previously collapsed.
39563      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39564      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39565      */
39566     /*
39567     expand : function(e, skipAnim){
39568         if(e) {
39569             e.stopPropagation();
39570         }
39571         if(!this.collapsed || this.el.hasActiveFx()) {
39572             return;
39573         }
39574         if(this.isSlid){
39575             this.afterSlideIn();
39576             skipAnim = true;
39577         }
39578         this.collapsed = false;
39579         if(this.config.animate && skipAnim !== true){
39580             this.animateExpand();
39581         }else{
39582             this.el.show();
39583             if(this.split){
39584                 this.split.el.show();
39585             }
39586             this.collapsedEl.setLocation(-2000,-2000);
39587             this.collapsedEl.hide();
39588             this.fireEvent("invalidated", this);
39589             this.fireEvent("expanded", this);
39590         }
39591     },
39592 */
39593     animateExpand : function(){
39594         // overridden
39595     },
39596
39597     initTabs : function()
39598     {
39599         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39600         
39601         var ts = new Roo.bootstrap.panel.Tabs({
39602             el: this.bodyEl.dom,
39603             region : this,
39604             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39605             disableTooltips: this.config.disableTabTips,
39606             toolbar : this.config.toolbar
39607         });
39608         
39609         if(this.config.hideTabs){
39610             ts.stripWrap.setDisplayed(false);
39611         }
39612         this.tabs = ts;
39613         ts.resizeTabs = this.config.resizeTabs === true;
39614         ts.minTabWidth = this.config.minTabWidth || 40;
39615         ts.maxTabWidth = this.config.maxTabWidth || 250;
39616         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39617         ts.monitorResize = false;
39618         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39619         ts.bodyEl.addClass('roo-layout-tabs-body');
39620         this.panels.each(this.initPanelAsTab, this);
39621     },
39622
39623     initPanelAsTab : function(panel){
39624         var ti = this.tabs.addTab(
39625             panel.getEl().id,
39626             panel.getTitle(),
39627             null,
39628             this.config.closeOnTab && panel.isClosable(),
39629             panel.tpl
39630         );
39631         if(panel.tabTip !== undefined){
39632             ti.setTooltip(panel.tabTip);
39633         }
39634         ti.on("activate", function(){
39635               this.setActivePanel(panel);
39636         }, this);
39637         
39638         if(this.config.closeOnTab){
39639             ti.on("beforeclose", function(t, e){
39640                 e.cancel = true;
39641                 this.remove(panel);
39642             }, this);
39643         }
39644         
39645         panel.tabItem = ti;
39646         
39647         return ti;
39648     },
39649
39650     updatePanelTitle : function(panel, title)
39651     {
39652         if(this.activePanel == panel){
39653             this.updateTitle(title);
39654         }
39655         if(this.tabs){
39656             var ti = this.tabs.getTab(panel.getEl().id);
39657             ti.setText(title);
39658             if(panel.tabTip !== undefined){
39659                 ti.setTooltip(panel.tabTip);
39660             }
39661         }
39662     },
39663
39664     updateTitle : function(title){
39665         if(this.titleTextEl && !this.config.title){
39666             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39667         }
39668     },
39669
39670     setActivePanel : function(panel)
39671     {
39672         panel = this.getPanel(panel);
39673         if(this.activePanel && this.activePanel != panel){
39674             if(this.activePanel.setActiveState(false) === false){
39675                 return;
39676             }
39677         }
39678         this.activePanel = panel;
39679         panel.setActiveState(true);
39680         if(this.panelSize){
39681             panel.setSize(this.panelSize.width, this.panelSize.height);
39682         }
39683         if(this.closeBtn){
39684             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39685         }
39686         this.updateTitle(panel.getTitle());
39687         if(this.tabs){
39688             this.fireEvent("invalidated", this);
39689         }
39690         this.fireEvent("panelactivated", this, panel);
39691     },
39692
39693     /**
39694      * Shows the specified panel.
39695      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39696      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39697      */
39698     showPanel : function(panel)
39699     {
39700         panel = this.getPanel(panel);
39701         if(panel){
39702             if(this.tabs){
39703                 var tab = this.tabs.getTab(panel.getEl().id);
39704                 if(tab.isHidden()){
39705                     this.tabs.unhideTab(tab.id);
39706                 }
39707                 tab.activate();
39708             }else{
39709                 this.setActivePanel(panel);
39710             }
39711         }
39712         return panel;
39713     },
39714
39715     /**
39716      * Get the active panel for this region.
39717      * @return {Roo.ContentPanel} The active panel or null
39718      */
39719     getActivePanel : function(){
39720         return this.activePanel;
39721     },
39722
39723     validateVisibility : function(){
39724         if(this.panels.getCount() < 1){
39725             this.updateTitle("&#160;");
39726             this.closeBtn.hide();
39727             this.hide();
39728         }else{
39729             if(!this.isVisible()){
39730                 this.show();
39731             }
39732         }
39733     },
39734
39735     /**
39736      * Adds the passed ContentPanel(s) to this region.
39737      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39738      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39739      */
39740     add : function(panel)
39741     {
39742         if(arguments.length > 1){
39743             for(var i = 0, len = arguments.length; i < len; i++) {
39744                 this.add(arguments[i]);
39745             }
39746             return null;
39747         }
39748         
39749         // if we have not been rendered yet, then we can not really do much of this..
39750         if (!this.bodyEl) {
39751             this.unrendered_panels.push(panel);
39752             return panel;
39753         }
39754         
39755         
39756         
39757         
39758         if(this.hasPanel(panel)){
39759             this.showPanel(panel);
39760             return panel;
39761         }
39762         panel.setRegion(this);
39763         this.panels.add(panel);
39764        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39765             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39766             // and hide them... ???
39767             this.bodyEl.dom.appendChild(panel.getEl().dom);
39768             if(panel.background !== true){
39769                 this.setActivePanel(panel);
39770             }
39771             this.fireEvent("paneladded", this, panel);
39772             return panel;
39773         }
39774         */
39775         if(!this.tabs){
39776             this.initTabs();
39777         }else{
39778             this.initPanelAsTab(panel);
39779         }
39780         
39781         
39782         if(panel.background !== true){
39783             this.tabs.activate(panel.getEl().id);
39784         }
39785         this.fireEvent("paneladded", this, panel);
39786         return panel;
39787     },
39788
39789     /**
39790      * Hides the tab for the specified panel.
39791      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39792      */
39793     hidePanel : function(panel){
39794         if(this.tabs && (panel = this.getPanel(panel))){
39795             this.tabs.hideTab(panel.getEl().id);
39796         }
39797     },
39798
39799     /**
39800      * Unhides the tab for a previously hidden panel.
39801      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39802      */
39803     unhidePanel : function(panel){
39804         if(this.tabs && (panel = this.getPanel(panel))){
39805             this.tabs.unhideTab(panel.getEl().id);
39806         }
39807     },
39808
39809     clearPanels : function(){
39810         while(this.panels.getCount() > 0){
39811              this.remove(this.panels.first());
39812         }
39813     },
39814
39815     /**
39816      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39817      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39818      * @param {Boolean} preservePanel Overrides the config preservePanel option
39819      * @return {Roo.ContentPanel} The panel that was removed
39820      */
39821     remove : function(panel, preservePanel)
39822     {
39823         panel = this.getPanel(panel);
39824         if(!panel){
39825             return null;
39826         }
39827         var e = {};
39828         this.fireEvent("beforeremove", this, panel, e);
39829         if(e.cancel === true){
39830             return null;
39831         }
39832         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39833         var panelId = panel.getId();
39834         this.panels.removeKey(panelId);
39835         if(preservePanel){
39836             document.body.appendChild(panel.getEl().dom);
39837         }
39838         if(this.tabs){
39839             this.tabs.removeTab(panel.getEl().id);
39840         }else if (!preservePanel){
39841             this.bodyEl.dom.removeChild(panel.getEl().dom);
39842         }
39843         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39844             var p = this.panels.first();
39845             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39846             tempEl.appendChild(p.getEl().dom);
39847             this.bodyEl.update("");
39848             this.bodyEl.dom.appendChild(p.getEl().dom);
39849             tempEl = null;
39850             this.updateTitle(p.getTitle());
39851             this.tabs = null;
39852             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39853             this.setActivePanel(p);
39854         }
39855         panel.setRegion(null);
39856         if(this.activePanel == panel){
39857             this.activePanel = null;
39858         }
39859         if(this.config.autoDestroy !== false && preservePanel !== true){
39860             try{panel.destroy();}catch(e){}
39861         }
39862         this.fireEvent("panelremoved", this, panel);
39863         return panel;
39864     },
39865
39866     /**
39867      * Returns the TabPanel component used by this region
39868      * @return {Roo.TabPanel}
39869      */
39870     getTabs : function(){
39871         return this.tabs;
39872     },
39873
39874     createTool : function(parentEl, className){
39875         var btn = Roo.DomHelper.append(parentEl, {
39876             tag: "div",
39877             cls: "x-layout-tools-button",
39878             children: [ {
39879                 tag: "div",
39880                 cls: "roo-layout-tools-button-inner " + className,
39881                 html: "&#160;"
39882             }]
39883         }, true);
39884         btn.addClassOnOver("roo-layout-tools-button-over");
39885         return btn;
39886     }
39887 });/*
39888  * Based on:
39889  * Ext JS Library 1.1.1
39890  * Copyright(c) 2006-2007, Ext JS, LLC.
39891  *
39892  * Originally Released Under LGPL - original licence link has changed is not relivant.
39893  *
39894  * Fork - LGPL
39895  * <script type="text/javascript">
39896  */
39897  
39898
39899
39900 /**
39901  * @class Roo.SplitLayoutRegion
39902  * @extends Roo.LayoutRegion
39903  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39904  */
39905 Roo.bootstrap.layout.Split = function(config){
39906     this.cursor = config.cursor;
39907     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39908 };
39909
39910 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39911 {
39912     splitTip : "Drag to resize.",
39913     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39914     useSplitTips : false,
39915
39916     applyConfig : function(config){
39917         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39918     },
39919     
39920     onRender : function(ctr,pos) {
39921         
39922         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39923         if(!this.config.split){
39924             return;
39925         }
39926         if(!this.split){
39927             
39928             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39929                             tag: "div",
39930                             id: this.el.id + "-split",
39931                             cls: "roo-layout-split roo-layout-split-"+this.position,
39932                             html: "&#160;"
39933             });
39934             /** The SplitBar for this region 
39935             * @type Roo.SplitBar */
39936             // does not exist yet...
39937             Roo.log([this.position, this.orientation]);
39938             
39939             this.split = new Roo.bootstrap.SplitBar({
39940                 dragElement : splitEl,
39941                 resizingElement: this.el,
39942                 orientation : this.orientation
39943             });
39944             
39945             this.split.on("moved", this.onSplitMove, this);
39946             this.split.useShim = this.config.useShim === true;
39947             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39948             if(this.useSplitTips){
39949                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39950             }
39951             //if(config.collapsible){
39952             //    this.split.el.on("dblclick", this.collapse,  this);
39953             //}
39954         }
39955         if(typeof this.config.minSize != "undefined"){
39956             this.split.minSize = this.config.minSize;
39957         }
39958         if(typeof this.config.maxSize != "undefined"){
39959             this.split.maxSize = this.config.maxSize;
39960         }
39961         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39962             this.hideSplitter();
39963         }
39964         
39965     },
39966
39967     getHMaxSize : function(){
39968          var cmax = this.config.maxSize || 10000;
39969          var center = this.mgr.getRegion("center");
39970          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39971     },
39972
39973     getVMaxSize : function(){
39974          var cmax = this.config.maxSize || 10000;
39975          var center = this.mgr.getRegion("center");
39976          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39977     },
39978
39979     onSplitMove : function(split, newSize){
39980         this.fireEvent("resized", this, newSize);
39981     },
39982     
39983     /** 
39984      * Returns the {@link Roo.SplitBar} for this region.
39985      * @return {Roo.SplitBar}
39986      */
39987     getSplitBar : function(){
39988         return this.split;
39989     },
39990     
39991     hide : function(){
39992         this.hideSplitter();
39993         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39994     },
39995
39996     hideSplitter : function(){
39997         if(this.split){
39998             this.split.el.setLocation(-2000,-2000);
39999             this.split.el.hide();
40000         }
40001     },
40002
40003     show : function(){
40004         if(this.split){
40005             this.split.el.show();
40006         }
40007         Roo.bootstrap.layout.Split.superclass.show.call(this);
40008     },
40009     
40010     beforeSlide: function(){
40011         if(Roo.isGecko){// firefox overflow auto bug workaround
40012             this.bodyEl.clip();
40013             if(this.tabs) {
40014                 this.tabs.bodyEl.clip();
40015             }
40016             if(this.activePanel){
40017                 this.activePanel.getEl().clip();
40018                 
40019                 if(this.activePanel.beforeSlide){
40020                     this.activePanel.beforeSlide();
40021                 }
40022             }
40023         }
40024     },
40025     
40026     afterSlide : function(){
40027         if(Roo.isGecko){// firefox overflow auto bug workaround
40028             this.bodyEl.unclip();
40029             if(this.tabs) {
40030                 this.tabs.bodyEl.unclip();
40031             }
40032             if(this.activePanel){
40033                 this.activePanel.getEl().unclip();
40034                 if(this.activePanel.afterSlide){
40035                     this.activePanel.afterSlide();
40036                 }
40037             }
40038         }
40039     },
40040
40041     initAutoHide : function(){
40042         if(this.autoHide !== false){
40043             if(!this.autoHideHd){
40044                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40045                 this.autoHideHd = {
40046                     "mouseout": function(e){
40047                         if(!e.within(this.el, true)){
40048                             st.delay(500);
40049                         }
40050                     },
40051                     "mouseover" : function(e){
40052                         st.cancel();
40053                     },
40054                     scope : this
40055                 };
40056             }
40057             this.el.on(this.autoHideHd);
40058         }
40059     },
40060
40061     clearAutoHide : function(){
40062         if(this.autoHide !== false){
40063             this.el.un("mouseout", this.autoHideHd.mouseout);
40064             this.el.un("mouseover", this.autoHideHd.mouseover);
40065         }
40066     },
40067
40068     clearMonitor : function(){
40069         Roo.get(document).un("click", this.slideInIf, this);
40070     },
40071
40072     // these names are backwards but not changed for compat
40073     slideOut : function(){
40074         if(this.isSlid || this.el.hasActiveFx()){
40075             return;
40076         }
40077         this.isSlid = true;
40078         if(this.collapseBtn){
40079             this.collapseBtn.hide();
40080         }
40081         this.closeBtnState = this.closeBtn.getStyle('display');
40082         this.closeBtn.hide();
40083         if(this.stickBtn){
40084             this.stickBtn.show();
40085         }
40086         this.el.show();
40087         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40088         this.beforeSlide();
40089         this.el.setStyle("z-index", 10001);
40090         this.el.slideIn(this.getSlideAnchor(), {
40091             callback: function(){
40092                 this.afterSlide();
40093                 this.initAutoHide();
40094                 Roo.get(document).on("click", this.slideInIf, this);
40095                 this.fireEvent("slideshow", this);
40096             },
40097             scope: this,
40098             block: true
40099         });
40100     },
40101
40102     afterSlideIn : function(){
40103         this.clearAutoHide();
40104         this.isSlid = false;
40105         this.clearMonitor();
40106         this.el.setStyle("z-index", "");
40107         if(this.collapseBtn){
40108             this.collapseBtn.show();
40109         }
40110         this.closeBtn.setStyle('display', this.closeBtnState);
40111         if(this.stickBtn){
40112             this.stickBtn.hide();
40113         }
40114         this.fireEvent("slidehide", this);
40115     },
40116
40117     slideIn : function(cb){
40118         if(!this.isSlid || this.el.hasActiveFx()){
40119             Roo.callback(cb);
40120             return;
40121         }
40122         this.isSlid = false;
40123         this.beforeSlide();
40124         this.el.slideOut(this.getSlideAnchor(), {
40125             callback: function(){
40126                 this.el.setLeftTop(-10000, -10000);
40127                 this.afterSlide();
40128                 this.afterSlideIn();
40129                 Roo.callback(cb);
40130             },
40131             scope: this,
40132             block: true
40133         });
40134     },
40135     
40136     slideInIf : function(e){
40137         if(!e.within(this.el)){
40138             this.slideIn();
40139         }
40140     },
40141
40142     animateCollapse : function(){
40143         this.beforeSlide();
40144         this.el.setStyle("z-index", 20000);
40145         var anchor = this.getSlideAnchor();
40146         this.el.slideOut(anchor, {
40147             callback : function(){
40148                 this.el.setStyle("z-index", "");
40149                 this.collapsedEl.slideIn(anchor, {duration:.3});
40150                 this.afterSlide();
40151                 this.el.setLocation(-10000,-10000);
40152                 this.el.hide();
40153                 this.fireEvent("collapsed", this);
40154             },
40155             scope: this,
40156             block: true
40157         });
40158     },
40159
40160     animateExpand : function(){
40161         this.beforeSlide();
40162         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40163         this.el.setStyle("z-index", 20000);
40164         this.collapsedEl.hide({
40165             duration:.1
40166         });
40167         this.el.slideIn(this.getSlideAnchor(), {
40168             callback : function(){
40169                 this.el.setStyle("z-index", "");
40170                 this.afterSlide();
40171                 if(this.split){
40172                     this.split.el.show();
40173                 }
40174                 this.fireEvent("invalidated", this);
40175                 this.fireEvent("expanded", this);
40176             },
40177             scope: this,
40178             block: true
40179         });
40180     },
40181
40182     anchors : {
40183         "west" : "left",
40184         "east" : "right",
40185         "north" : "top",
40186         "south" : "bottom"
40187     },
40188
40189     sanchors : {
40190         "west" : "l",
40191         "east" : "r",
40192         "north" : "t",
40193         "south" : "b"
40194     },
40195
40196     canchors : {
40197         "west" : "tl-tr",
40198         "east" : "tr-tl",
40199         "north" : "tl-bl",
40200         "south" : "bl-tl"
40201     },
40202
40203     getAnchor : function(){
40204         return this.anchors[this.position];
40205     },
40206
40207     getCollapseAnchor : function(){
40208         return this.canchors[this.position];
40209     },
40210
40211     getSlideAnchor : function(){
40212         return this.sanchors[this.position];
40213     },
40214
40215     getAlignAdj : function(){
40216         var cm = this.cmargins;
40217         switch(this.position){
40218             case "west":
40219                 return [0, 0];
40220             break;
40221             case "east":
40222                 return [0, 0];
40223             break;
40224             case "north":
40225                 return [0, 0];
40226             break;
40227             case "south":
40228                 return [0, 0];
40229             break;
40230         }
40231     },
40232
40233     getExpandAdj : function(){
40234         var c = this.collapsedEl, cm = this.cmargins;
40235         switch(this.position){
40236             case "west":
40237                 return [-(cm.right+c.getWidth()+cm.left), 0];
40238             break;
40239             case "east":
40240                 return [cm.right+c.getWidth()+cm.left, 0];
40241             break;
40242             case "north":
40243                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40244             break;
40245             case "south":
40246                 return [0, cm.top+cm.bottom+c.getHeight()];
40247             break;
40248         }
40249     }
40250 });/*
40251  * Based on:
40252  * Ext JS Library 1.1.1
40253  * Copyright(c) 2006-2007, Ext JS, LLC.
40254  *
40255  * Originally Released Under LGPL - original licence link has changed is not relivant.
40256  *
40257  * Fork - LGPL
40258  * <script type="text/javascript">
40259  */
40260 /*
40261  * These classes are private internal classes
40262  */
40263 Roo.bootstrap.layout.Center = function(config){
40264     config.region = "center";
40265     Roo.bootstrap.layout.Region.call(this, config);
40266     this.visible = true;
40267     this.minWidth = config.minWidth || 20;
40268     this.minHeight = config.minHeight || 20;
40269 };
40270
40271 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40272     hide : function(){
40273         // center panel can't be hidden
40274     },
40275     
40276     show : function(){
40277         // center panel can't be hidden
40278     },
40279     
40280     getMinWidth: function(){
40281         return this.minWidth;
40282     },
40283     
40284     getMinHeight: function(){
40285         return this.minHeight;
40286     }
40287 });
40288
40289
40290
40291
40292  
40293
40294
40295
40296
40297
40298
40299 Roo.bootstrap.layout.North = function(config)
40300 {
40301     config.region = 'north';
40302     config.cursor = 'n-resize';
40303     
40304     Roo.bootstrap.layout.Split.call(this, config);
40305     
40306     
40307     if(this.split){
40308         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40309         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40310         this.split.el.addClass("roo-layout-split-v");
40311     }
40312     //var size = config.initialSize || config.height;
40313     //if(this.el && typeof size != "undefined"){
40314     //    this.el.setHeight(size);
40315     //}
40316 };
40317 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40318 {
40319     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40320      
40321      
40322     onRender : function(ctr, pos)
40323     {
40324         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40325         var size = this.config.initialSize || this.config.height;
40326         if(this.el && typeof size != "undefined"){
40327             this.el.setHeight(size);
40328         }
40329     
40330     },
40331     
40332     getBox : function(){
40333         if(this.collapsed){
40334             return this.collapsedEl.getBox();
40335         }
40336         var box = this.el.getBox();
40337         if(this.split){
40338             box.height += this.split.el.getHeight();
40339         }
40340         return box;
40341     },
40342     
40343     updateBox : function(box){
40344         if(this.split && !this.collapsed){
40345             box.height -= this.split.el.getHeight();
40346             this.split.el.setLeft(box.x);
40347             this.split.el.setTop(box.y+box.height);
40348             this.split.el.setWidth(box.width);
40349         }
40350         if(this.collapsed){
40351             this.updateBody(box.width, null);
40352         }
40353         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40354     }
40355 });
40356
40357
40358
40359
40360
40361 Roo.bootstrap.layout.South = function(config){
40362     config.region = 'south';
40363     config.cursor = 's-resize';
40364     Roo.bootstrap.layout.Split.call(this, config);
40365     if(this.split){
40366         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40367         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40368         this.split.el.addClass("roo-layout-split-v");
40369     }
40370     
40371 };
40372
40373 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40374     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40375     
40376     onRender : function(ctr, pos)
40377     {
40378         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40379         var size = this.config.initialSize || this.config.height;
40380         if(this.el && typeof size != "undefined"){
40381             this.el.setHeight(size);
40382         }
40383     
40384     },
40385     
40386     getBox : function(){
40387         if(this.collapsed){
40388             return this.collapsedEl.getBox();
40389         }
40390         var box = this.el.getBox();
40391         if(this.split){
40392             var sh = this.split.el.getHeight();
40393             box.height += sh;
40394             box.y -= sh;
40395         }
40396         return box;
40397     },
40398     
40399     updateBox : function(box){
40400         if(this.split && !this.collapsed){
40401             var sh = this.split.el.getHeight();
40402             box.height -= sh;
40403             box.y += sh;
40404             this.split.el.setLeft(box.x);
40405             this.split.el.setTop(box.y-sh);
40406             this.split.el.setWidth(box.width);
40407         }
40408         if(this.collapsed){
40409             this.updateBody(box.width, null);
40410         }
40411         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40412     }
40413 });
40414
40415 Roo.bootstrap.layout.East = function(config){
40416     config.region = "east";
40417     config.cursor = "e-resize";
40418     Roo.bootstrap.layout.Split.call(this, config);
40419     if(this.split){
40420         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40421         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40422         this.split.el.addClass("roo-layout-split-h");
40423     }
40424     
40425 };
40426 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40427     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40428     
40429     onRender : function(ctr, pos)
40430     {
40431         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40432         var size = this.config.initialSize || this.config.width;
40433         if(this.el && typeof size != "undefined"){
40434             this.el.setWidth(size);
40435         }
40436     
40437     },
40438     
40439     getBox : function(){
40440         if(this.collapsed){
40441             return this.collapsedEl.getBox();
40442         }
40443         var box = this.el.getBox();
40444         if(this.split){
40445             var sw = this.split.el.getWidth();
40446             box.width += sw;
40447             box.x -= sw;
40448         }
40449         return box;
40450     },
40451
40452     updateBox : function(box){
40453         if(this.split && !this.collapsed){
40454             var sw = this.split.el.getWidth();
40455             box.width -= sw;
40456             this.split.el.setLeft(box.x);
40457             this.split.el.setTop(box.y);
40458             this.split.el.setHeight(box.height);
40459             box.x += sw;
40460         }
40461         if(this.collapsed){
40462             this.updateBody(null, box.height);
40463         }
40464         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40465     }
40466 });
40467
40468 Roo.bootstrap.layout.West = function(config){
40469     config.region = "west";
40470     config.cursor = "w-resize";
40471     
40472     Roo.bootstrap.layout.Split.call(this, config);
40473     if(this.split){
40474         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40475         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40476         this.split.el.addClass("roo-layout-split-h");
40477     }
40478     
40479 };
40480 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40481     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40482     
40483     onRender: function(ctr, pos)
40484     {
40485         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40486         var size = this.config.initialSize || this.config.width;
40487         if(typeof size != "undefined"){
40488             this.el.setWidth(size);
40489         }
40490     },
40491     
40492     getBox : function(){
40493         if(this.collapsed){
40494             return this.collapsedEl.getBox();
40495         }
40496         var box = this.el.getBox();
40497         if (box.width == 0) {
40498             box.width = this.config.width; // kludge?
40499         }
40500         if(this.split){
40501             box.width += this.split.el.getWidth();
40502         }
40503         return box;
40504     },
40505     
40506     updateBox : function(box){
40507         if(this.split && !this.collapsed){
40508             var sw = this.split.el.getWidth();
40509             box.width -= sw;
40510             this.split.el.setLeft(box.x+box.width);
40511             this.split.el.setTop(box.y);
40512             this.split.el.setHeight(box.height);
40513         }
40514         if(this.collapsed){
40515             this.updateBody(null, box.height);
40516         }
40517         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40518     }
40519 });Roo.namespace("Roo.bootstrap.panel");/*
40520  * Based on:
40521  * Ext JS Library 1.1.1
40522  * Copyright(c) 2006-2007, Ext JS, LLC.
40523  *
40524  * Originally Released Under LGPL - original licence link has changed is not relivant.
40525  *
40526  * Fork - LGPL
40527  * <script type="text/javascript">
40528  */
40529 /**
40530  * @class Roo.ContentPanel
40531  * @extends Roo.util.Observable
40532  * A basic ContentPanel element.
40533  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40534  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40535  * @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
40536  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40537  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40538  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40539  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40540  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40541  * @cfg {String} title          The title for this panel
40542  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40543  * @cfg {String} url            Calls {@link #setUrl} with this value
40544  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40545  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40546  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40547  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40548  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40549  * @cfg {Boolean} badges render the badges
40550  * @cfg {String} cls  extra classes to use  
40551  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40552
40553  * @constructor
40554  * Create a new ContentPanel.
40555  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40556  * @param {String/Object} config A string to set only the title or a config object
40557  * @param {String} content (optional) Set the HTML content for this panel
40558  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40559  */
40560 Roo.bootstrap.panel.Content = function( config){
40561     
40562     this.tpl = config.tpl || false;
40563     
40564     var el = config.el;
40565     var content = config.content;
40566
40567     if(config.autoCreate){ // xtype is available if this is called from factory
40568         el = Roo.id();
40569     }
40570     this.el = Roo.get(el);
40571     if(!this.el && config && config.autoCreate){
40572         if(typeof config.autoCreate == "object"){
40573             if(!config.autoCreate.id){
40574                 config.autoCreate.id = config.id||el;
40575             }
40576             this.el = Roo.DomHelper.append(document.body,
40577                         config.autoCreate, true);
40578         }else{
40579             var elcfg =  {
40580                 tag: "div",
40581                 cls: (config.cls || '') +
40582                     (config.background ? ' bg-' + config.background : '') +
40583                     " roo-layout-inactive-content",
40584                 id: config.id||el
40585             };
40586             if (config.iframe) {
40587                 elcfg.cn = [
40588                     {
40589                         tag : 'iframe',
40590                         style : 'border: 0px',
40591                         src : 'about:blank'
40592                     }
40593                 ];
40594             }
40595               
40596             if (config.html) {
40597                 elcfg.html = config.html;
40598                 
40599             }
40600                         
40601             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40602             if (config.iframe) {
40603                 this.iframeEl = this.el.select('iframe',true).first();
40604             }
40605             
40606         }
40607     } 
40608     this.closable = false;
40609     this.loaded = false;
40610     this.active = false;
40611    
40612       
40613     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40614         
40615         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40616         
40617         this.wrapEl = this.el; //this.el.wrap();
40618         var ti = [];
40619         if (config.toolbar.items) {
40620             ti = config.toolbar.items ;
40621             delete config.toolbar.items ;
40622         }
40623         
40624         var nitems = [];
40625         this.toolbar.render(this.wrapEl, 'before');
40626         for(var i =0;i < ti.length;i++) {
40627           //  Roo.log(['add child', items[i]]);
40628             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40629         }
40630         this.toolbar.items = nitems;
40631         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40632         delete config.toolbar;
40633         
40634     }
40635     /*
40636     // xtype created footer. - not sure if will work as we normally have to render first..
40637     if (this.footer && !this.footer.el && this.footer.xtype) {
40638         if (!this.wrapEl) {
40639             this.wrapEl = this.el.wrap();
40640         }
40641     
40642         this.footer.container = this.wrapEl.createChild();
40643          
40644         this.footer = Roo.factory(this.footer, Roo);
40645         
40646     }
40647     */
40648     
40649      if(typeof config == "string"){
40650         this.title = config;
40651     }else{
40652         Roo.apply(this, config);
40653     }
40654     
40655     if(this.resizeEl){
40656         this.resizeEl = Roo.get(this.resizeEl, true);
40657     }else{
40658         this.resizeEl = this.el;
40659     }
40660     // handle view.xtype
40661     
40662  
40663     
40664     
40665     this.addEvents({
40666         /**
40667          * @event activate
40668          * Fires when this panel is activated. 
40669          * @param {Roo.ContentPanel} this
40670          */
40671         "activate" : true,
40672         /**
40673          * @event deactivate
40674          * Fires when this panel is activated. 
40675          * @param {Roo.ContentPanel} this
40676          */
40677         "deactivate" : true,
40678
40679         /**
40680          * @event resize
40681          * Fires when this panel is resized if fitToFrame is true.
40682          * @param {Roo.ContentPanel} this
40683          * @param {Number} width The width after any component adjustments
40684          * @param {Number} height The height after any component adjustments
40685          */
40686         "resize" : true,
40687         
40688          /**
40689          * @event render
40690          * Fires when this tab is created
40691          * @param {Roo.ContentPanel} this
40692          */
40693         "render" : true,
40694         
40695           /**
40696          * @event scroll
40697          * Fires when this content is scrolled
40698          * @param {Roo.ContentPanel} this
40699          * @param {Event} scrollEvent
40700          */
40701         "scroll" : true
40702         
40703         
40704         
40705     });
40706     
40707
40708     
40709     
40710     if(this.autoScroll && !this.iframe){
40711         this.resizeEl.setStyle("overflow", "auto");
40712         this.resizeEl.on('scroll', this.onScroll, this);
40713     } else {
40714         // fix randome scrolling
40715         //this.el.on('scroll', function() {
40716         //    Roo.log('fix random scolling');
40717         //    this.scrollTo('top',0); 
40718         //});
40719     }
40720     content = content || this.content;
40721     if(content){
40722         this.setContent(content);
40723     }
40724     if(config && config.url){
40725         this.setUrl(this.url, this.params, this.loadOnce);
40726     }
40727     
40728     
40729     
40730     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40731     
40732     if (this.view && typeof(this.view.xtype) != 'undefined') {
40733         this.view.el = this.el.appendChild(document.createElement("div"));
40734         this.view = Roo.factory(this.view); 
40735         this.view.render  &&  this.view.render(false, '');  
40736     }
40737     
40738     
40739     this.fireEvent('render', this);
40740 };
40741
40742 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40743     
40744     cls : '',
40745     background : '',
40746     
40747     tabTip : '',
40748     
40749     iframe : false,
40750     iframeEl : false,
40751     
40752     /* Resize Element - use this to work out scroll etc. */
40753     resizeEl : false,
40754     
40755     setRegion : function(region){
40756         this.region = region;
40757         this.setActiveClass(region && !this.background);
40758     },
40759     
40760     
40761     setActiveClass: function(state)
40762     {
40763         if(state){
40764            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40765            this.el.setStyle('position','relative');
40766         }else{
40767            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40768            this.el.setStyle('position', 'absolute');
40769         } 
40770     },
40771     
40772     /**
40773      * Returns the toolbar for this Panel if one was configured. 
40774      * @return {Roo.Toolbar} 
40775      */
40776     getToolbar : function(){
40777         return this.toolbar;
40778     },
40779     
40780     setActiveState : function(active)
40781     {
40782         this.active = active;
40783         this.setActiveClass(active);
40784         if(!active){
40785             if(this.fireEvent("deactivate", this) === false){
40786                 return false;
40787             }
40788             return true;
40789         }
40790         this.fireEvent("activate", this);
40791         return true;
40792     },
40793     /**
40794      * Updates this panel's element (not for iframe)
40795      * @param {String} content The new content
40796      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40797     */
40798     setContent : function(content, loadScripts){
40799         if (this.iframe) {
40800             return;
40801         }
40802         
40803         this.el.update(content, loadScripts);
40804     },
40805
40806     ignoreResize : function(w, h){
40807         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40808             return true;
40809         }else{
40810             this.lastSize = {width: w, height: h};
40811             return false;
40812         }
40813     },
40814     /**
40815      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40816      * @return {Roo.UpdateManager} The UpdateManager
40817      */
40818     getUpdateManager : function(){
40819         if (this.iframe) {
40820             return false;
40821         }
40822         return this.el.getUpdateManager();
40823     },
40824      /**
40825      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40826      * Does not work with IFRAME contents
40827      * @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:
40828 <pre><code>
40829 panel.load({
40830     url: "your-url.php",
40831     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40832     callback: yourFunction,
40833     scope: yourObject, //(optional scope)
40834     discardUrl: false,
40835     nocache: false,
40836     text: "Loading...",
40837     timeout: 30,
40838     scripts: false
40839 });
40840 </code></pre>
40841      
40842      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40843      * 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.
40844      * @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}
40845      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40846      * @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.
40847      * @return {Roo.ContentPanel} this
40848      */
40849     load : function(){
40850         
40851         if (this.iframe) {
40852             return this;
40853         }
40854         
40855         var um = this.el.getUpdateManager();
40856         um.update.apply(um, arguments);
40857         return this;
40858     },
40859
40860
40861     /**
40862      * 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.
40863      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40864      * @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)
40865      * @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)
40866      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40867      */
40868     setUrl : function(url, params, loadOnce){
40869         if (this.iframe) {
40870             this.iframeEl.dom.src = url;
40871             return false;
40872         }
40873         
40874         if(this.refreshDelegate){
40875             this.removeListener("activate", this.refreshDelegate);
40876         }
40877         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40878         this.on("activate", this.refreshDelegate);
40879         return this.el.getUpdateManager();
40880     },
40881     
40882     _handleRefresh : function(url, params, loadOnce){
40883         if(!loadOnce || !this.loaded){
40884             var updater = this.el.getUpdateManager();
40885             updater.update(url, params, this._setLoaded.createDelegate(this));
40886         }
40887     },
40888     
40889     _setLoaded : function(){
40890         this.loaded = true;
40891     }, 
40892     
40893     /**
40894      * Returns this panel's id
40895      * @return {String} 
40896      */
40897     getId : function(){
40898         return this.el.id;
40899     },
40900     
40901     /** 
40902      * Returns this panel's element - used by regiosn to add.
40903      * @return {Roo.Element} 
40904      */
40905     getEl : function(){
40906         return this.wrapEl || this.el;
40907     },
40908     
40909    
40910     
40911     adjustForComponents : function(width, height)
40912     {
40913         //Roo.log('adjustForComponents ');
40914         if(this.resizeEl != this.el){
40915             width -= this.el.getFrameWidth('lr');
40916             height -= this.el.getFrameWidth('tb');
40917         }
40918         if(this.toolbar){
40919             var te = this.toolbar.getEl();
40920             te.setWidth(width);
40921             height -= te.getHeight();
40922         }
40923         if(this.footer){
40924             var te = this.footer.getEl();
40925             te.setWidth(width);
40926             height -= te.getHeight();
40927         }
40928         
40929         
40930         if(this.adjustments){
40931             width += this.adjustments[0];
40932             height += this.adjustments[1];
40933         }
40934         return {"width": width, "height": height};
40935     },
40936     
40937     setSize : function(width, height){
40938         if(this.fitToFrame && !this.ignoreResize(width, height)){
40939             if(this.fitContainer && this.resizeEl != this.el){
40940                 this.el.setSize(width, height);
40941             }
40942             var size = this.adjustForComponents(width, height);
40943             if (this.iframe) {
40944                 this.iframeEl.setSize(width,height);
40945             }
40946             
40947             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40948             this.fireEvent('resize', this, size.width, size.height);
40949             
40950             
40951         }
40952     },
40953     
40954     /**
40955      * Returns this panel's title
40956      * @return {String} 
40957      */
40958     getTitle : function(){
40959         
40960         if (typeof(this.title) != 'object') {
40961             return this.title;
40962         }
40963         
40964         var t = '';
40965         for (var k in this.title) {
40966             if (!this.title.hasOwnProperty(k)) {
40967                 continue;
40968             }
40969             
40970             if (k.indexOf('-') >= 0) {
40971                 var s = k.split('-');
40972                 for (var i = 0; i<s.length; i++) {
40973                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40974                 }
40975             } else {
40976                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40977             }
40978         }
40979         return t;
40980     },
40981     
40982     /**
40983      * Set this panel's title
40984      * @param {String} title
40985      */
40986     setTitle : function(title){
40987         this.title = title;
40988         if(this.region){
40989             this.region.updatePanelTitle(this, title);
40990         }
40991     },
40992     
40993     /**
40994      * Returns true is this panel was configured to be closable
40995      * @return {Boolean} 
40996      */
40997     isClosable : function(){
40998         return this.closable;
40999     },
41000     
41001     beforeSlide : function(){
41002         this.el.clip();
41003         this.resizeEl.clip();
41004     },
41005     
41006     afterSlide : function(){
41007         this.el.unclip();
41008         this.resizeEl.unclip();
41009     },
41010     
41011     /**
41012      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41013      *   Will fail silently if the {@link #setUrl} method has not been called.
41014      *   This does not activate the panel, just updates its content.
41015      */
41016     refresh : function(){
41017         if(this.refreshDelegate){
41018            this.loaded = false;
41019            this.refreshDelegate();
41020         }
41021     },
41022     
41023     /**
41024      * Destroys this panel
41025      */
41026     destroy : function(){
41027         this.el.removeAllListeners();
41028         var tempEl = document.createElement("span");
41029         tempEl.appendChild(this.el.dom);
41030         tempEl.innerHTML = "";
41031         this.el.remove();
41032         this.el = null;
41033     },
41034     
41035     /**
41036      * form - if the content panel contains a form - this is a reference to it.
41037      * @type {Roo.form.Form}
41038      */
41039     form : false,
41040     /**
41041      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41042      *    This contains a reference to it.
41043      * @type {Roo.View}
41044      */
41045     view : false,
41046     
41047       /**
41048      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41049      * <pre><code>
41050
41051 layout.addxtype({
41052        xtype : 'Form',
41053        items: [ .... ]
41054    }
41055 );
41056
41057 </code></pre>
41058      * @param {Object} cfg Xtype definition of item to add.
41059      */
41060     
41061     
41062     getChildContainer: function () {
41063         return this.getEl();
41064     },
41065     
41066     
41067     onScroll : function(e)
41068     {
41069         this.fireEvent('scroll', this, e);
41070     }
41071     
41072     
41073     /*
41074         var  ret = new Roo.factory(cfg);
41075         return ret;
41076         
41077         
41078         // add form..
41079         if (cfg.xtype.match(/^Form$/)) {
41080             
41081             var el;
41082             //if (this.footer) {
41083             //    el = this.footer.container.insertSibling(false, 'before');
41084             //} else {
41085                 el = this.el.createChild();
41086             //}
41087
41088             this.form = new  Roo.form.Form(cfg);
41089             
41090             
41091             if ( this.form.allItems.length) {
41092                 this.form.render(el.dom);
41093             }
41094             return this.form;
41095         }
41096         // should only have one of theses..
41097         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41098             // views.. should not be just added - used named prop 'view''
41099             
41100             cfg.el = this.el.appendChild(document.createElement("div"));
41101             // factory?
41102             
41103             var ret = new Roo.factory(cfg);
41104              
41105              ret.render && ret.render(false, ''); // render blank..
41106             this.view = ret;
41107             return ret;
41108         }
41109         return false;
41110     }
41111     \*/
41112 });
41113  
41114 /**
41115  * @class Roo.bootstrap.panel.Grid
41116  * @extends Roo.bootstrap.panel.Content
41117  * @constructor
41118  * Create a new GridPanel.
41119  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41120  * @param {Object} config A the config object
41121   
41122  */
41123
41124
41125
41126 Roo.bootstrap.panel.Grid = function(config)
41127 {
41128     
41129       
41130     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41131         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41132
41133     config.el = this.wrapper;
41134     //this.el = this.wrapper;
41135     
41136       if (config.container) {
41137         // ctor'ed from a Border/panel.grid
41138         
41139         
41140         this.wrapper.setStyle("overflow", "hidden");
41141         this.wrapper.addClass('roo-grid-container');
41142
41143     }
41144     
41145     
41146     if(config.toolbar){
41147         var tool_el = this.wrapper.createChild();    
41148         this.toolbar = Roo.factory(config.toolbar);
41149         var ti = [];
41150         if (config.toolbar.items) {
41151             ti = config.toolbar.items ;
41152             delete config.toolbar.items ;
41153         }
41154         
41155         var nitems = [];
41156         this.toolbar.render(tool_el);
41157         for(var i =0;i < ti.length;i++) {
41158           //  Roo.log(['add child', items[i]]);
41159             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41160         }
41161         this.toolbar.items = nitems;
41162         
41163         delete config.toolbar;
41164     }
41165     
41166     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41167     config.grid.scrollBody = true;;
41168     config.grid.monitorWindowResize = false; // turn off autosizing
41169     config.grid.autoHeight = false;
41170     config.grid.autoWidth = false;
41171     
41172     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41173     
41174     if (config.background) {
41175         // render grid on panel activation (if panel background)
41176         this.on('activate', function(gp) {
41177             if (!gp.grid.rendered) {
41178                 gp.grid.render(this.wrapper);
41179                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41180             }
41181         });
41182             
41183     } else {
41184         this.grid.render(this.wrapper);
41185         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41186
41187     }
41188     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41189     // ??? needed ??? config.el = this.wrapper;
41190     
41191     
41192     
41193   
41194     // xtype created footer. - not sure if will work as we normally have to render first..
41195     if (this.footer && !this.footer.el && this.footer.xtype) {
41196         
41197         var ctr = this.grid.getView().getFooterPanel(true);
41198         this.footer.dataSource = this.grid.dataSource;
41199         this.footer = Roo.factory(this.footer, Roo);
41200         this.footer.render(ctr);
41201         
41202     }
41203     
41204     
41205     
41206     
41207      
41208 };
41209
41210 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41211     getId : function(){
41212         return this.grid.id;
41213     },
41214     
41215     /**
41216      * Returns the grid for this panel
41217      * @return {Roo.bootstrap.Table} 
41218      */
41219     getGrid : function(){
41220         return this.grid;    
41221     },
41222     
41223     setSize : function(width, height){
41224         if(!this.ignoreResize(width, height)){
41225             var grid = this.grid;
41226             var size = this.adjustForComponents(width, height);
41227             // tfoot is not a footer?
41228           
41229             
41230             var gridel = grid.getGridEl();
41231             gridel.setSize(size.width, size.height);
41232             
41233             var tbd = grid.getGridEl().select('tbody', true).first();
41234             var thd = grid.getGridEl().select('thead',true).first();
41235             var tbf= grid.getGridEl().select('tfoot', true).first();
41236
41237             if (tbf) {
41238                 size.height -= tbf.getHeight();
41239             }
41240             if (thd) {
41241                 size.height -= thd.getHeight();
41242             }
41243             
41244             tbd.setSize(size.width, size.height );
41245             // this is for the account management tab -seems to work there.
41246             var thd = grid.getGridEl().select('thead',true).first();
41247             //if (tbd) {
41248             //    tbd.setSize(size.width, size.height - thd.getHeight());
41249             //}
41250              
41251             grid.autoSize();
41252         }
41253     },
41254      
41255     
41256     
41257     beforeSlide : function(){
41258         this.grid.getView().scroller.clip();
41259     },
41260     
41261     afterSlide : function(){
41262         this.grid.getView().scroller.unclip();
41263     },
41264     
41265     destroy : function(){
41266         this.grid.destroy();
41267         delete this.grid;
41268         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41269     }
41270 });
41271
41272 /**
41273  * @class Roo.bootstrap.panel.Nest
41274  * @extends Roo.bootstrap.panel.Content
41275  * @constructor
41276  * Create a new Panel, that can contain a layout.Border.
41277  * 
41278  * 
41279  * @param {Roo.BorderLayout} layout The layout for this panel
41280  * @param {String/Object} config A string to set only the title or a config object
41281  */
41282 Roo.bootstrap.panel.Nest = function(config)
41283 {
41284     // construct with only one argument..
41285     /* FIXME - implement nicer consturctors
41286     if (layout.layout) {
41287         config = layout;
41288         layout = config.layout;
41289         delete config.layout;
41290     }
41291     if (layout.xtype && !layout.getEl) {
41292         // then layout needs constructing..
41293         layout = Roo.factory(layout, Roo);
41294     }
41295     */
41296     
41297     config.el =  config.layout.getEl();
41298     
41299     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41300     
41301     config.layout.monitorWindowResize = false; // turn off autosizing
41302     this.layout = config.layout;
41303     this.layout.getEl().addClass("roo-layout-nested-layout");
41304     this.layout.parent = this;
41305     
41306     
41307     
41308     
41309 };
41310
41311 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41312
41313     setSize : function(width, height){
41314         if(!this.ignoreResize(width, height)){
41315             var size = this.adjustForComponents(width, height);
41316             var el = this.layout.getEl();
41317             if (size.height < 1) {
41318                 el.setWidth(size.width);   
41319             } else {
41320                 el.setSize(size.width, size.height);
41321             }
41322             var touch = el.dom.offsetWidth;
41323             this.layout.layout();
41324             // ie requires a double layout on the first pass
41325             if(Roo.isIE && !this.initialized){
41326                 this.initialized = true;
41327                 this.layout.layout();
41328             }
41329         }
41330     },
41331     
41332     // activate all subpanels if not currently active..
41333     
41334     setActiveState : function(active){
41335         this.active = active;
41336         this.setActiveClass(active);
41337         
41338         if(!active){
41339             this.fireEvent("deactivate", this);
41340             return;
41341         }
41342         
41343         this.fireEvent("activate", this);
41344         // not sure if this should happen before or after..
41345         if (!this.layout) {
41346             return; // should not happen..
41347         }
41348         var reg = false;
41349         for (var r in this.layout.regions) {
41350             reg = this.layout.getRegion(r);
41351             if (reg.getActivePanel()) {
41352                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41353                 reg.setActivePanel(reg.getActivePanel());
41354                 continue;
41355             }
41356             if (!reg.panels.length) {
41357                 continue;
41358             }
41359             reg.showPanel(reg.getPanel(0));
41360         }
41361         
41362         
41363         
41364         
41365     },
41366     
41367     /**
41368      * Returns the nested BorderLayout for this panel
41369      * @return {Roo.BorderLayout} 
41370      */
41371     getLayout : function(){
41372         return this.layout;
41373     },
41374     
41375      /**
41376      * Adds a xtype elements to the layout of the nested panel
41377      * <pre><code>
41378
41379 panel.addxtype({
41380        xtype : 'ContentPanel',
41381        region: 'west',
41382        items: [ .... ]
41383    }
41384 );
41385
41386 panel.addxtype({
41387         xtype : 'NestedLayoutPanel',
41388         region: 'west',
41389         layout: {
41390            center: { },
41391            west: { }   
41392         },
41393         items : [ ... list of content panels or nested layout panels.. ]
41394    }
41395 );
41396 </code></pre>
41397      * @param {Object} cfg Xtype definition of item to add.
41398      */
41399     addxtype : function(cfg) {
41400         return this.layout.addxtype(cfg);
41401     
41402     }
41403 });/*
41404  * Based on:
41405  * Ext JS Library 1.1.1
41406  * Copyright(c) 2006-2007, Ext JS, LLC.
41407  *
41408  * Originally Released Under LGPL - original licence link has changed is not relivant.
41409  *
41410  * Fork - LGPL
41411  * <script type="text/javascript">
41412  */
41413 /**
41414  * @class Roo.TabPanel
41415  * @extends Roo.util.Observable
41416  * A lightweight tab container.
41417  * <br><br>
41418  * Usage:
41419  * <pre><code>
41420 // basic tabs 1, built from existing content
41421 var tabs = new Roo.TabPanel("tabs1");
41422 tabs.addTab("script", "View Script");
41423 tabs.addTab("markup", "View Markup");
41424 tabs.activate("script");
41425
41426 // more advanced tabs, built from javascript
41427 var jtabs = new Roo.TabPanel("jtabs");
41428 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41429
41430 // set up the UpdateManager
41431 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41432 var updater = tab2.getUpdateManager();
41433 updater.setDefaultUrl("ajax1.htm");
41434 tab2.on('activate', updater.refresh, updater, true);
41435
41436 // Use setUrl for Ajax loading
41437 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41438 tab3.setUrl("ajax2.htm", null, true);
41439
41440 // Disabled tab
41441 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41442 tab4.disable();
41443
41444 jtabs.activate("jtabs-1");
41445  * </code></pre>
41446  * @constructor
41447  * Create a new TabPanel.
41448  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41449  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41450  */
41451 Roo.bootstrap.panel.Tabs = function(config){
41452     /**
41453     * The container element for this TabPanel.
41454     * @type Roo.Element
41455     */
41456     this.el = Roo.get(config.el);
41457     delete config.el;
41458     if(config){
41459         if(typeof config == "boolean"){
41460             this.tabPosition = config ? "bottom" : "top";
41461         }else{
41462             Roo.apply(this, config);
41463         }
41464     }
41465     
41466     if(this.tabPosition == "bottom"){
41467         // if tabs are at the bottom = create the body first.
41468         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41469         this.el.addClass("roo-tabs-bottom");
41470     }
41471     // next create the tabs holders
41472     
41473     if (this.tabPosition == "west"){
41474         
41475         var reg = this.region; // fake it..
41476         while (reg) {
41477             if (!reg.mgr.parent) {
41478                 break;
41479             }
41480             reg = reg.mgr.parent.region;
41481         }
41482         Roo.log("got nest?");
41483         Roo.log(reg);
41484         if (reg.mgr.getRegion('west')) {
41485             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41486             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41487             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41488             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41489             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41490         
41491             
41492         }
41493         
41494         
41495     } else {
41496      
41497         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41498         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41499         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41500         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41501     }
41502     
41503     
41504     if(Roo.isIE){
41505         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41506     }
41507     
41508     // finally - if tabs are at the top, then create the body last..
41509     if(this.tabPosition != "bottom"){
41510         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41511          * @type Roo.Element
41512          */
41513         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41514         this.el.addClass("roo-tabs-top");
41515     }
41516     this.items = [];
41517
41518     this.bodyEl.setStyle("position", "relative");
41519
41520     this.active = null;
41521     this.activateDelegate = this.activate.createDelegate(this);
41522
41523     this.addEvents({
41524         /**
41525          * @event tabchange
41526          * Fires when the active tab changes
41527          * @param {Roo.TabPanel} this
41528          * @param {Roo.TabPanelItem} activePanel The new active tab
41529          */
41530         "tabchange": true,
41531         /**
41532          * @event beforetabchange
41533          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41534          * @param {Roo.TabPanel} this
41535          * @param {Object} e Set cancel to true on this object to cancel the tab change
41536          * @param {Roo.TabPanelItem} tab The tab being changed to
41537          */
41538         "beforetabchange" : true
41539     });
41540
41541     Roo.EventManager.onWindowResize(this.onResize, this);
41542     this.cpad = this.el.getPadding("lr");
41543     this.hiddenCount = 0;
41544
41545
41546     // toolbar on the tabbar support...
41547     if (this.toolbar) {
41548         alert("no toolbar support yet");
41549         this.toolbar  = false;
41550         /*
41551         var tcfg = this.toolbar;
41552         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41553         this.toolbar = new Roo.Toolbar(tcfg);
41554         if (Roo.isSafari) {
41555             var tbl = tcfg.container.child('table', true);
41556             tbl.setAttribute('width', '100%');
41557         }
41558         */
41559         
41560     }
41561    
41562
41563
41564     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41565 };
41566
41567 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41568     /*
41569      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41570      */
41571     tabPosition : "top",
41572     /*
41573      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41574      */
41575     currentTabWidth : 0,
41576     /*
41577      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41578      */
41579     minTabWidth : 40,
41580     /*
41581      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41582      */
41583     maxTabWidth : 250,
41584     /*
41585      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41586      */
41587     preferredTabWidth : 175,
41588     /*
41589      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41590      */
41591     resizeTabs : false,
41592     /*
41593      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41594      */
41595     monitorResize : true,
41596     /*
41597      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41598      */
41599     toolbar : false,  // set by caller..
41600     
41601     region : false, /// set by caller
41602     
41603     disableTooltips : true, // not used yet...
41604
41605     /**
41606      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41607      * @param {String} id The id of the div to use <b>or create</b>
41608      * @param {String} text The text for the tab
41609      * @param {String} content (optional) Content to put in the TabPanelItem body
41610      * @param {Boolean} closable (optional) True to create a close icon on the tab
41611      * @return {Roo.TabPanelItem} The created TabPanelItem
41612      */
41613     addTab : function(id, text, content, closable, tpl)
41614     {
41615         var item = new Roo.bootstrap.panel.TabItem({
41616             panel: this,
41617             id : id,
41618             text : text,
41619             closable : closable,
41620             tpl : tpl
41621         });
41622         this.addTabItem(item);
41623         if(content){
41624             item.setContent(content);
41625         }
41626         return item;
41627     },
41628
41629     /**
41630      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41631      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41632      * @return {Roo.TabPanelItem}
41633      */
41634     getTab : function(id){
41635         return this.items[id];
41636     },
41637
41638     /**
41639      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41640      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41641      */
41642     hideTab : function(id){
41643         var t = this.items[id];
41644         if(!t.isHidden()){
41645            t.setHidden(true);
41646            this.hiddenCount++;
41647            this.autoSizeTabs();
41648         }
41649     },
41650
41651     /**
41652      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41653      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41654      */
41655     unhideTab : function(id){
41656         var t = this.items[id];
41657         if(t.isHidden()){
41658            t.setHidden(false);
41659            this.hiddenCount--;
41660            this.autoSizeTabs();
41661         }
41662     },
41663
41664     /**
41665      * Adds an existing {@link Roo.TabPanelItem}.
41666      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41667      */
41668     addTabItem : function(item)
41669     {
41670         this.items[item.id] = item;
41671         this.items.push(item);
41672         this.autoSizeTabs();
41673       //  if(this.resizeTabs){
41674     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41675   //         this.autoSizeTabs();
41676 //        }else{
41677 //            item.autoSize();
41678        // }
41679     },
41680
41681     /**
41682      * Removes a {@link Roo.TabPanelItem}.
41683      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41684      */
41685     removeTab : function(id){
41686         var items = this.items;
41687         var tab = items[id];
41688         if(!tab) { return; }
41689         var index = items.indexOf(tab);
41690         if(this.active == tab && items.length > 1){
41691             var newTab = this.getNextAvailable(index);
41692             if(newTab) {
41693                 newTab.activate();
41694             }
41695         }
41696         this.stripEl.dom.removeChild(tab.pnode.dom);
41697         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41698             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41699         }
41700         items.splice(index, 1);
41701         delete this.items[tab.id];
41702         tab.fireEvent("close", tab);
41703         tab.purgeListeners();
41704         this.autoSizeTabs();
41705     },
41706
41707     getNextAvailable : function(start){
41708         var items = this.items;
41709         var index = start;
41710         // look for a next tab that will slide over to
41711         // replace the one being removed
41712         while(index < items.length){
41713             var item = items[++index];
41714             if(item && !item.isHidden()){
41715                 return item;
41716             }
41717         }
41718         // if one isn't found select the previous tab (on the left)
41719         index = start;
41720         while(index >= 0){
41721             var item = items[--index];
41722             if(item && !item.isHidden()){
41723                 return item;
41724             }
41725         }
41726         return null;
41727     },
41728
41729     /**
41730      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41731      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41732      */
41733     disableTab : function(id){
41734         var tab = this.items[id];
41735         if(tab && this.active != tab){
41736             tab.disable();
41737         }
41738     },
41739
41740     /**
41741      * Enables a {@link Roo.TabPanelItem} that is disabled.
41742      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41743      */
41744     enableTab : function(id){
41745         var tab = this.items[id];
41746         tab.enable();
41747     },
41748
41749     /**
41750      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41751      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41752      * @return {Roo.TabPanelItem} The TabPanelItem.
41753      */
41754     activate : function(id)
41755     {
41756         //Roo.log('activite:'  + id);
41757         
41758         var tab = this.items[id];
41759         if(!tab){
41760             return null;
41761         }
41762         if(tab == this.active || tab.disabled){
41763             return tab;
41764         }
41765         var e = {};
41766         this.fireEvent("beforetabchange", this, e, tab);
41767         if(e.cancel !== true && !tab.disabled){
41768             if(this.active){
41769                 this.active.hide();
41770             }
41771             this.active = this.items[id];
41772             this.active.show();
41773             this.fireEvent("tabchange", this, this.active);
41774         }
41775         return tab;
41776     },
41777
41778     /**
41779      * Gets the active {@link Roo.TabPanelItem}.
41780      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41781      */
41782     getActiveTab : function(){
41783         return this.active;
41784     },
41785
41786     /**
41787      * Updates the tab body element to fit the height of the container element
41788      * for overflow scrolling
41789      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41790      */
41791     syncHeight : function(targetHeight){
41792         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41793         var bm = this.bodyEl.getMargins();
41794         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41795         this.bodyEl.setHeight(newHeight);
41796         return newHeight;
41797     },
41798
41799     onResize : function(){
41800         if(this.monitorResize){
41801             this.autoSizeTabs();
41802         }
41803     },
41804
41805     /**
41806      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41807      */
41808     beginUpdate : function(){
41809         this.updating = true;
41810     },
41811
41812     /**
41813      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41814      */
41815     endUpdate : function(){
41816         this.updating = false;
41817         this.autoSizeTabs();
41818     },
41819
41820     /**
41821      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41822      */
41823     autoSizeTabs : function()
41824     {
41825         var count = this.items.length;
41826         var vcount = count - this.hiddenCount;
41827         
41828         if (vcount < 2) {
41829             this.stripEl.hide();
41830         } else {
41831             this.stripEl.show();
41832         }
41833         
41834         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41835             return;
41836         }
41837         
41838         
41839         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41840         var availWidth = Math.floor(w / vcount);
41841         var b = this.stripBody;
41842         if(b.getWidth() > w){
41843             var tabs = this.items;
41844             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41845             if(availWidth < this.minTabWidth){
41846                 /*if(!this.sleft){    // incomplete scrolling code
41847                     this.createScrollButtons();
41848                 }
41849                 this.showScroll();
41850                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41851             }
41852         }else{
41853             if(this.currentTabWidth < this.preferredTabWidth){
41854                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41855             }
41856         }
41857     },
41858
41859     /**
41860      * Returns the number of tabs in this TabPanel.
41861      * @return {Number}
41862      */
41863      getCount : function(){
41864          return this.items.length;
41865      },
41866
41867     /**
41868      * Resizes all the tabs to the passed width
41869      * @param {Number} The new width
41870      */
41871     setTabWidth : function(width){
41872         this.currentTabWidth = width;
41873         for(var i = 0, len = this.items.length; i < len; i++) {
41874                 if(!this.items[i].isHidden()) {
41875                 this.items[i].setWidth(width);
41876             }
41877         }
41878     },
41879
41880     /**
41881      * Destroys this TabPanel
41882      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41883      */
41884     destroy : function(removeEl){
41885         Roo.EventManager.removeResizeListener(this.onResize, this);
41886         for(var i = 0, len = this.items.length; i < len; i++){
41887             this.items[i].purgeListeners();
41888         }
41889         if(removeEl === true){
41890             this.el.update("");
41891             this.el.remove();
41892         }
41893     },
41894     
41895     createStrip : function(container)
41896     {
41897         var strip = document.createElement("nav");
41898         strip.className = Roo.bootstrap.version == 4 ?
41899             "navbar-light bg-light" : 
41900             "navbar navbar-default"; //"x-tabs-wrap";
41901         container.appendChild(strip);
41902         return strip;
41903     },
41904     
41905     createStripList : function(strip)
41906     {
41907         // div wrapper for retard IE
41908         // returns the "tr" element.
41909         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41910         //'<div class="x-tabs-strip-wrap">'+
41911           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41912           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41913         return strip.firstChild; //.firstChild.firstChild.firstChild;
41914     },
41915     createBody : function(container)
41916     {
41917         var body = document.createElement("div");
41918         Roo.id(body, "tab-body");
41919         //Roo.fly(body).addClass("x-tabs-body");
41920         Roo.fly(body).addClass("tab-content");
41921         container.appendChild(body);
41922         return body;
41923     },
41924     createItemBody :function(bodyEl, id){
41925         var body = Roo.getDom(id);
41926         if(!body){
41927             body = document.createElement("div");
41928             body.id = id;
41929         }
41930         //Roo.fly(body).addClass("x-tabs-item-body");
41931         Roo.fly(body).addClass("tab-pane");
41932          bodyEl.insertBefore(body, bodyEl.firstChild);
41933         return body;
41934     },
41935     /** @private */
41936     createStripElements :  function(stripEl, text, closable, tpl)
41937     {
41938         var td = document.createElement("li"); // was td..
41939         td.className = 'nav-item';
41940         
41941         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41942         
41943         
41944         stripEl.appendChild(td);
41945         /*if(closable){
41946             td.className = "x-tabs-closable";
41947             if(!this.closeTpl){
41948                 this.closeTpl = new Roo.Template(
41949                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41950                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41951                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41952                 );
41953             }
41954             var el = this.closeTpl.overwrite(td, {"text": text});
41955             var close = el.getElementsByTagName("div")[0];
41956             var inner = el.getElementsByTagName("em")[0];
41957             return {"el": el, "close": close, "inner": inner};
41958         } else {
41959         */
41960         // not sure what this is..
41961 //            if(!this.tabTpl){
41962                 //this.tabTpl = new Roo.Template(
41963                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41964                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41965                 //);
41966 //                this.tabTpl = new Roo.Template(
41967 //                   '<a href="#">' +
41968 //                   '<span unselectable="on"' +
41969 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41970 //                            ' >{text}</span></a>'
41971 //                );
41972 //                
41973 //            }
41974
41975
41976             var template = tpl || this.tabTpl || false;
41977             
41978             if(!template){
41979                 template =  new Roo.Template(
41980                         Roo.bootstrap.version == 4 ? 
41981                             (
41982                                 '<a class="nav-link" href="#" unselectable="on"' +
41983                                      (this.disableTooltips ? '' : ' title="{text}"') +
41984                                      ' >{text}</a>'
41985                             ) : (
41986                                 '<a class="nav-link" href="#">' +
41987                                 '<span unselectable="on"' +
41988                                          (this.disableTooltips ? '' : ' title="{text}"') +
41989                                     ' >{text}</span></a>'
41990                             )
41991                 );
41992             }
41993             
41994             switch (typeof(template)) {
41995                 case 'object' :
41996                     break;
41997                 case 'string' :
41998                     template = new Roo.Template(template);
41999                     break;
42000                 default :
42001                     break;
42002             }
42003             
42004             var el = template.overwrite(td, {"text": text});
42005             
42006             var inner = el.getElementsByTagName("span")[0];
42007             
42008             return {"el": el, "inner": inner};
42009             
42010     }
42011         
42012     
42013 });
42014
42015 /**
42016  * @class Roo.TabPanelItem
42017  * @extends Roo.util.Observable
42018  * Represents an individual item (tab plus body) in a TabPanel.
42019  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42020  * @param {String} id The id of this TabPanelItem
42021  * @param {String} text The text for the tab of this TabPanelItem
42022  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42023  */
42024 Roo.bootstrap.panel.TabItem = function(config){
42025     /**
42026      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42027      * @type Roo.TabPanel
42028      */
42029     this.tabPanel = config.panel;
42030     /**
42031      * The id for this TabPanelItem
42032      * @type String
42033      */
42034     this.id = config.id;
42035     /** @private */
42036     this.disabled = false;
42037     /** @private */
42038     this.text = config.text;
42039     /** @private */
42040     this.loaded = false;
42041     this.closable = config.closable;
42042
42043     /**
42044      * The body element for this TabPanelItem.
42045      * @type Roo.Element
42046      */
42047     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42048     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42049     this.bodyEl.setStyle("display", "block");
42050     this.bodyEl.setStyle("zoom", "1");
42051     //this.hideAction();
42052
42053     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42054     /** @private */
42055     this.el = Roo.get(els.el);
42056     this.inner = Roo.get(els.inner, true);
42057      this.textEl = Roo.bootstrap.version == 4 ?
42058         this.el : Roo.get(this.el.dom.firstChild, true);
42059
42060     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42061     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42062
42063     
42064 //    this.el.on("mousedown", this.onTabMouseDown, this);
42065     this.el.on("click", this.onTabClick, this);
42066     /** @private */
42067     if(config.closable){
42068         var c = Roo.get(els.close, true);
42069         c.dom.title = this.closeText;
42070         c.addClassOnOver("close-over");
42071         c.on("click", this.closeClick, this);
42072      }
42073
42074     this.addEvents({
42075          /**
42076          * @event activate
42077          * Fires when this tab becomes the active tab.
42078          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42079          * @param {Roo.TabPanelItem} this
42080          */
42081         "activate": true,
42082         /**
42083          * @event beforeclose
42084          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42085          * @param {Roo.TabPanelItem} this
42086          * @param {Object} e Set cancel to true on this object to cancel the close.
42087          */
42088         "beforeclose": true,
42089         /**
42090          * @event close
42091          * Fires when this tab is closed.
42092          * @param {Roo.TabPanelItem} this
42093          */
42094          "close": true,
42095         /**
42096          * @event deactivate
42097          * Fires when this tab is no longer the active tab.
42098          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42099          * @param {Roo.TabPanelItem} this
42100          */
42101          "deactivate" : true
42102     });
42103     this.hidden = false;
42104
42105     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42106 };
42107
42108 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42109            {
42110     purgeListeners : function(){
42111        Roo.util.Observable.prototype.purgeListeners.call(this);
42112        this.el.removeAllListeners();
42113     },
42114     /**
42115      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42116      */
42117     show : function(){
42118         this.status_node.addClass("active");
42119         this.showAction();
42120         if(Roo.isOpera){
42121             this.tabPanel.stripWrap.repaint();
42122         }
42123         this.fireEvent("activate", this.tabPanel, this);
42124     },
42125
42126     /**
42127      * Returns true if this tab is the active tab.
42128      * @return {Boolean}
42129      */
42130     isActive : function(){
42131         return this.tabPanel.getActiveTab() == this;
42132     },
42133
42134     /**
42135      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42136      */
42137     hide : function(){
42138         this.status_node.removeClass("active");
42139         this.hideAction();
42140         this.fireEvent("deactivate", this.tabPanel, this);
42141     },
42142
42143     hideAction : function(){
42144         this.bodyEl.hide();
42145         this.bodyEl.setStyle("position", "absolute");
42146         this.bodyEl.setLeft("-20000px");
42147         this.bodyEl.setTop("-20000px");
42148     },
42149
42150     showAction : function(){
42151         this.bodyEl.setStyle("position", "relative");
42152         this.bodyEl.setTop("");
42153         this.bodyEl.setLeft("");
42154         this.bodyEl.show();
42155     },
42156
42157     /**
42158      * Set the tooltip for the tab.
42159      * @param {String} tooltip The tab's tooltip
42160      */
42161     setTooltip : function(text){
42162         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42163             this.textEl.dom.qtip = text;
42164             this.textEl.dom.removeAttribute('title');
42165         }else{
42166             this.textEl.dom.title = text;
42167         }
42168     },
42169
42170     onTabClick : function(e){
42171         e.preventDefault();
42172         this.tabPanel.activate(this.id);
42173     },
42174
42175     onTabMouseDown : function(e){
42176         e.preventDefault();
42177         this.tabPanel.activate(this.id);
42178     },
42179 /*
42180     getWidth : function(){
42181         return this.inner.getWidth();
42182     },
42183
42184     setWidth : function(width){
42185         var iwidth = width - this.linode.getPadding("lr");
42186         this.inner.setWidth(iwidth);
42187         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42188         this.linode.setWidth(width);
42189     },
42190 */
42191     /**
42192      * Show or hide the tab
42193      * @param {Boolean} hidden True to hide or false to show.
42194      */
42195     setHidden : function(hidden){
42196         this.hidden = hidden;
42197         this.linode.setStyle("display", hidden ? "none" : "");
42198     },
42199
42200     /**
42201      * Returns true if this tab is "hidden"
42202      * @return {Boolean}
42203      */
42204     isHidden : function(){
42205         return this.hidden;
42206     },
42207
42208     /**
42209      * Returns the text for this tab
42210      * @return {String}
42211      */
42212     getText : function(){
42213         return this.text;
42214     },
42215     /*
42216     autoSize : function(){
42217         //this.el.beginMeasure();
42218         this.textEl.setWidth(1);
42219         /*
42220          *  #2804 [new] Tabs in Roojs
42221          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42222          */
42223         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42224         //this.el.endMeasure();
42225     //},
42226
42227     /**
42228      * Sets the text for the tab (Note: this also sets the tooltip text)
42229      * @param {String} text The tab's text and tooltip
42230      */
42231     setText : function(text){
42232         this.text = text;
42233         this.textEl.update(text);
42234         this.setTooltip(text);
42235         //if(!this.tabPanel.resizeTabs){
42236         //    this.autoSize();
42237         //}
42238     },
42239     /**
42240      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42241      */
42242     activate : function(){
42243         this.tabPanel.activate(this.id);
42244     },
42245
42246     /**
42247      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42248      */
42249     disable : function(){
42250         if(this.tabPanel.active != this){
42251             this.disabled = true;
42252             this.status_node.addClass("disabled");
42253         }
42254     },
42255
42256     /**
42257      * Enables this TabPanelItem if it was previously disabled.
42258      */
42259     enable : function(){
42260         this.disabled = false;
42261         this.status_node.removeClass("disabled");
42262     },
42263
42264     /**
42265      * Sets the content for this TabPanelItem.
42266      * @param {String} content The content
42267      * @param {Boolean} loadScripts true to look for and load scripts
42268      */
42269     setContent : function(content, loadScripts){
42270         this.bodyEl.update(content, loadScripts);
42271     },
42272
42273     /**
42274      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42275      * @return {Roo.UpdateManager} The UpdateManager
42276      */
42277     getUpdateManager : function(){
42278         return this.bodyEl.getUpdateManager();
42279     },
42280
42281     /**
42282      * Set a URL to be used to load the content for this TabPanelItem.
42283      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42284      * @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)
42285      * @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)
42286      * @return {Roo.UpdateManager} The UpdateManager
42287      */
42288     setUrl : function(url, params, loadOnce){
42289         if(this.refreshDelegate){
42290             this.un('activate', this.refreshDelegate);
42291         }
42292         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42293         this.on("activate", this.refreshDelegate);
42294         return this.bodyEl.getUpdateManager();
42295     },
42296
42297     /** @private */
42298     _handleRefresh : function(url, params, loadOnce){
42299         if(!loadOnce || !this.loaded){
42300             var updater = this.bodyEl.getUpdateManager();
42301             updater.update(url, params, this._setLoaded.createDelegate(this));
42302         }
42303     },
42304
42305     /**
42306      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42307      *   Will fail silently if the setUrl method has not been called.
42308      *   This does not activate the panel, just updates its content.
42309      */
42310     refresh : function(){
42311         if(this.refreshDelegate){
42312            this.loaded = false;
42313            this.refreshDelegate();
42314         }
42315     },
42316
42317     /** @private */
42318     _setLoaded : function(){
42319         this.loaded = true;
42320     },
42321
42322     /** @private */
42323     closeClick : function(e){
42324         var o = {};
42325         e.stopEvent();
42326         this.fireEvent("beforeclose", this, o);
42327         if(o.cancel !== true){
42328             this.tabPanel.removeTab(this.id);
42329         }
42330     },
42331     /**
42332      * The text displayed in the tooltip for the close icon.
42333      * @type String
42334      */
42335     closeText : "Close this tab"
42336 });
42337 /**
42338 *    This script refer to:
42339 *    Title: International Telephone Input
42340 *    Author: Jack O'Connor
42341 *    Code version:  v12.1.12
42342 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42343 **/
42344
42345 Roo.bootstrap.PhoneInputData = function() {
42346     var d = [
42347       [
42348         "Afghanistan (‫افغانستان‬‎)",
42349         "af",
42350         "93"
42351       ],
42352       [
42353         "Albania (Shqipëri)",
42354         "al",
42355         "355"
42356       ],
42357       [
42358         "Algeria (‫الجزائر‬‎)",
42359         "dz",
42360         "213"
42361       ],
42362       [
42363         "American Samoa",
42364         "as",
42365         "1684"
42366       ],
42367       [
42368         "Andorra",
42369         "ad",
42370         "376"
42371       ],
42372       [
42373         "Angola",
42374         "ao",
42375         "244"
42376       ],
42377       [
42378         "Anguilla",
42379         "ai",
42380         "1264"
42381       ],
42382       [
42383         "Antigua and Barbuda",
42384         "ag",
42385         "1268"
42386       ],
42387       [
42388         "Argentina",
42389         "ar",
42390         "54"
42391       ],
42392       [
42393         "Armenia (Հայաստան)",
42394         "am",
42395         "374"
42396       ],
42397       [
42398         "Aruba",
42399         "aw",
42400         "297"
42401       ],
42402       [
42403         "Australia",
42404         "au",
42405         "61",
42406         0
42407       ],
42408       [
42409         "Austria (Österreich)",
42410         "at",
42411         "43"
42412       ],
42413       [
42414         "Azerbaijan (Azərbaycan)",
42415         "az",
42416         "994"
42417       ],
42418       [
42419         "Bahamas",
42420         "bs",
42421         "1242"
42422       ],
42423       [
42424         "Bahrain (‫البحرين‬‎)",
42425         "bh",
42426         "973"
42427       ],
42428       [
42429         "Bangladesh (বাংলাদেশ)",
42430         "bd",
42431         "880"
42432       ],
42433       [
42434         "Barbados",
42435         "bb",
42436         "1246"
42437       ],
42438       [
42439         "Belarus (Беларусь)",
42440         "by",
42441         "375"
42442       ],
42443       [
42444         "Belgium (België)",
42445         "be",
42446         "32"
42447       ],
42448       [
42449         "Belize",
42450         "bz",
42451         "501"
42452       ],
42453       [
42454         "Benin (Bénin)",
42455         "bj",
42456         "229"
42457       ],
42458       [
42459         "Bermuda",
42460         "bm",
42461         "1441"
42462       ],
42463       [
42464         "Bhutan (འབྲུག)",
42465         "bt",
42466         "975"
42467       ],
42468       [
42469         "Bolivia",
42470         "bo",
42471         "591"
42472       ],
42473       [
42474         "Bosnia and Herzegovina (Босна и Херцеговина)",
42475         "ba",
42476         "387"
42477       ],
42478       [
42479         "Botswana",
42480         "bw",
42481         "267"
42482       ],
42483       [
42484         "Brazil (Brasil)",
42485         "br",
42486         "55"
42487       ],
42488       [
42489         "British Indian Ocean Territory",
42490         "io",
42491         "246"
42492       ],
42493       [
42494         "British Virgin Islands",
42495         "vg",
42496         "1284"
42497       ],
42498       [
42499         "Brunei",
42500         "bn",
42501         "673"
42502       ],
42503       [
42504         "Bulgaria (България)",
42505         "bg",
42506         "359"
42507       ],
42508       [
42509         "Burkina Faso",
42510         "bf",
42511         "226"
42512       ],
42513       [
42514         "Burundi (Uburundi)",
42515         "bi",
42516         "257"
42517       ],
42518       [
42519         "Cambodia (កម្ពុជា)",
42520         "kh",
42521         "855"
42522       ],
42523       [
42524         "Cameroon (Cameroun)",
42525         "cm",
42526         "237"
42527       ],
42528       [
42529         "Canada",
42530         "ca",
42531         "1",
42532         1,
42533         ["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"]
42534       ],
42535       [
42536         "Cape Verde (Kabu Verdi)",
42537         "cv",
42538         "238"
42539       ],
42540       [
42541         "Caribbean Netherlands",
42542         "bq",
42543         "599",
42544         1
42545       ],
42546       [
42547         "Cayman Islands",
42548         "ky",
42549         "1345"
42550       ],
42551       [
42552         "Central African Republic (République centrafricaine)",
42553         "cf",
42554         "236"
42555       ],
42556       [
42557         "Chad (Tchad)",
42558         "td",
42559         "235"
42560       ],
42561       [
42562         "Chile",
42563         "cl",
42564         "56"
42565       ],
42566       [
42567         "China (中国)",
42568         "cn",
42569         "86"
42570       ],
42571       [
42572         "Christmas Island",
42573         "cx",
42574         "61",
42575         2
42576       ],
42577       [
42578         "Cocos (Keeling) Islands",
42579         "cc",
42580         "61",
42581         1
42582       ],
42583       [
42584         "Colombia",
42585         "co",
42586         "57"
42587       ],
42588       [
42589         "Comoros (‫جزر القمر‬‎)",
42590         "km",
42591         "269"
42592       ],
42593       [
42594         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42595         "cd",
42596         "243"
42597       ],
42598       [
42599         "Congo (Republic) (Congo-Brazzaville)",
42600         "cg",
42601         "242"
42602       ],
42603       [
42604         "Cook Islands",
42605         "ck",
42606         "682"
42607       ],
42608       [
42609         "Costa Rica",
42610         "cr",
42611         "506"
42612       ],
42613       [
42614         "Côte d’Ivoire",
42615         "ci",
42616         "225"
42617       ],
42618       [
42619         "Croatia (Hrvatska)",
42620         "hr",
42621         "385"
42622       ],
42623       [
42624         "Cuba",
42625         "cu",
42626         "53"
42627       ],
42628       [
42629         "Curaçao",
42630         "cw",
42631         "599",
42632         0
42633       ],
42634       [
42635         "Cyprus (Κύπρος)",
42636         "cy",
42637         "357"
42638       ],
42639       [
42640         "Czech Republic (Česká republika)",
42641         "cz",
42642         "420"
42643       ],
42644       [
42645         "Denmark (Danmark)",
42646         "dk",
42647         "45"
42648       ],
42649       [
42650         "Djibouti",
42651         "dj",
42652         "253"
42653       ],
42654       [
42655         "Dominica",
42656         "dm",
42657         "1767"
42658       ],
42659       [
42660         "Dominican Republic (República Dominicana)",
42661         "do",
42662         "1",
42663         2,
42664         ["809", "829", "849"]
42665       ],
42666       [
42667         "Ecuador",
42668         "ec",
42669         "593"
42670       ],
42671       [
42672         "Egypt (‫مصر‬‎)",
42673         "eg",
42674         "20"
42675       ],
42676       [
42677         "El Salvador",
42678         "sv",
42679         "503"
42680       ],
42681       [
42682         "Equatorial Guinea (Guinea Ecuatorial)",
42683         "gq",
42684         "240"
42685       ],
42686       [
42687         "Eritrea",
42688         "er",
42689         "291"
42690       ],
42691       [
42692         "Estonia (Eesti)",
42693         "ee",
42694         "372"
42695       ],
42696       [
42697         "Ethiopia",
42698         "et",
42699         "251"
42700       ],
42701       [
42702         "Falkland Islands (Islas Malvinas)",
42703         "fk",
42704         "500"
42705       ],
42706       [
42707         "Faroe Islands (Føroyar)",
42708         "fo",
42709         "298"
42710       ],
42711       [
42712         "Fiji",
42713         "fj",
42714         "679"
42715       ],
42716       [
42717         "Finland (Suomi)",
42718         "fi",
42719         "358",
42720         0
42721       ],
42722       [
42723         "France",
42724         "fr",
42725         "33"
42726       ],
42727       [
42728         "French Guiana (Guyane française)",
42729         "gf",
42730         "594"
42731       ],
42732       [
42733         "French Polynesia (Polynésie française)",
42734         "pf",
42735         "689"
42736       ],
42737       [
42738         "Gabon",
42739         "ga",
42740         "241"
42741       ],
42742       [
42743         "Gambia",
42744         "gm",
42745         "220"
42746       ],
42747       [
42748         "Georgia (საქართველო)",
42749         "ge",
42750         "995"
42751       ],
42752       [
42753         "Germany (Deutschland)",
42754         "de",
42755         "49"
42756       ],
42757       [
42758         "Ghana (Gaana)",
42759         "gh",
42760         "233"
42761       ],
42762       [
42763         "Gibraltar",
42764         "gi",
42765         "350"
42766       ],
42767       [
42768         "Greece (Ελλάδα)",
42769         "gr",
42770         "30"
42771       ],
42772       [
42773         "Greenland (Kalaallit Nunaat)",
42774         "gl",
42775         "299"
42776       ],
42777       [
42778         "Grenada",
42779         "gd",
42780         "1473"
42781       ],
42782       [
42783         "Guadeloupe",
42784         "gp",
42785         "590",
42786         0
42787       ],
42788       [
42789         "Guam",
42790         "gu",
42791         "1671"
42792       ],
42793       [
42794         "Guatemala",
42795         "gt",
42796         "502"
42797       ],
42798       [
42799         "Guernsey",
42800         "gg",
42801         "44",
42802         1
42803       ],
42804       [
42805         "Guinea (Guinée)",
42806         "gn",
42807         "224"
42808       ],
42809       [
42810         "Guinea-Bissau (Guiné Bissau)",
42811         "gw",
42812         "245"
42813       ],
42814       [
42815         "Guyana",
42816         "gy",
42817         "592"
42818       ],
42819       [
42820         "Haiti",
42821         "ht",
42822         "509"
42823       ],
42824       [
42825         "Honduras",
42826         "hn",
42827         "504"
42828       ],
42829       [
42830         "Hong Kong (香港)",
42831         "hk",
42832         "852"
42833       ],
42834       [
42835         "Hungary (Magyarország)",
42836         "hu",
42837         "36"
42838       ],
42839       [
42840         "Iceland (Ísland)",
42841         "is",
42842         "354"
42843       ],
42844       [
42845         "India (भारत)",
42846         "in",
42847         "91"
42848       ],
42849       [
42850         "Indonesia",
42851         "id",
42852         "62"
42853       ],
42854       [
42855         "Iran (‫ایران‬‎)",
42856         "ir",
42857         "98"
42858       ],
42859       [
42860         "Iraq (‫العراق‬‎)",
42861         "iq",
42862         "964"
42863       ],
42864       [
42865         "Ireland",
42866         "ie",
42867         "353"
42868       ],
42869       [
42870         "Isle of Man",
42871         "im",
42872         "44",
42873         2
42874       ],
42875       [
42876         "Israel (‫ישראל‬‎)",
42877         "il",
42878         "972"
42879       ],
42880       [
42881         "Italy (Italia)",
42882         "it",
42883         "39",
42884         0
42885       ],
42886       [
42887         "Jamaica",
42888         "jm",
42889         "1876"
42890       ],
42891       [
42892         "Japan (日本)",
42893         "jp",
42894         "81"
42895       ],
42896       [
42897         "Jersey",
42898         "je",
42899         "44",
42900         3
42901       ],
42902       [
42903         "Jordan (‫الأردن‬‎)",
42904         "jo",
42905         "962"
42906       ],
42907       [
42908         "Kazakhstan (Казахстан)",
42909         "kz",
42910         "7",
42911         1
42912       ],
42913       [
42914         "Kenya",
42915         "ke",
42916         "254"
42917       ],
42918       [
42919         "Kiribati",
42920         "ki",
42921         "686"
42922       ],
42923       [
42924         "Kosovo",
42925         "xk",
42926         "383"
42927       ],
42928       [
42929         "Kuwait (‫الكويت‬‎)",
42930         "kw",
42931         "965"
42932       ],
42933       [
42934         "Kyrgyzstan (Кыргызстан)",
42935         "kg",
42936         "996"
42937       ],
42938       [
42939         "Laos (ລາວ)",
42940         "la",
42941         "856"
42942       ],
42943       [
42944         "Latvia (Latvija)",
42945         "lv",
42946         "371"
42947       ],
42948       [
42949         "Lebanon (‫لبنان‬‎)",
42950         "lb",
42951         "961"
42952       ],
42953       [
42954         "Lesotho",
42955         "ls",
42956         "266"
42957       ],
42958       [
42959         "Liberia",
42960         "lr",
42961         "231"
42962       ],
42963       [
42964         "Libya (‫ليبيا‬‎)",
42965         "ly",
42966         "218"
42967       ],
42968       [
42969         "Liechtenstein",
42970         "li",
42971         "423"
42972       ],
42973       [
42974         "Lithuania (Lietuva)",
42975         "lt",
42976         "370"
42977       ],
42978       [
42979         "Luxembourg",
42980         "lu",
42981         "352"
42982       ],
42983       [
42984         "Macau (澳門)",
42985         "mo",
42986         "853"
42987       ],
42988       [
42989         "Macedonia (FYROM) (Македонија)",
42990         "mk",
42991         "389"
42992       ],
42993       [
42994         "Madagascar (Madagasikara)",
42995         "mg",
42996         "261"
42997       ],
42998       [
42999         "Malawi",
43000         "mw",
43001         "265"
43002       ],
43003       [
43004         "Malaysia",
43005         "my",
43006         "60"
43007       ],
43008       [
43009         "Maldives",
43010         "mv",
43011         "960"
43012       ],
43013       [
43014         "Mali",
43015         "ml",
43016         "223"
43017       ],
43018       [
43019         "Malta",
43020         "mt",
43021         "356"
43022       ],
43023       [
43024         "Marshall Islands",
43025         "mh",
43026         "692"
43027       ],
43028       [
43029         "Martinique",
43030         "mq",
43031         "596"
43032       ],
43033       [
43034         "Mauritania (‫موريتانيا‬‎)",
43035         "mr",
43036         "222"
43037       ],
43038       [
43039         "Mauritius (Moris)",
43040         "mu",
43041         "230"
43042       ],
43043       [
43044         "Mayotte",
43045         "yt",
43046         "262",
43047         1
43048       ],
43049       [
43050         "Mexico (México)",
43051         "mx",
43052         "52"
43053       ],
43054       [
43055         "Micronesia",
43056         "fm",
43057         "691"
43058       ],
43059       [
43060         "Moldova (Republica Moldova)",
43061         "md",
43062         "373"
43063       ],
43064       [
43065         "Monaco",
43066         "mc",
43067         "377"
43068       ],
43069       [
43070         "Mongolia (Монгол)",
43071         "mn",
43072         "976"
43073       ],
43074       [
43075         "Montenegro (Crna Gora)",
43076         "me",
43077         "382"
43078       ],
43079       [
43080         "Montserrat",
43081         "ms",
43082         "1664"
43083       ],
43084       [
43085         "Morocco (‫المغرب‬‎)",
43086         "ma",
43087         "212",
43088         0
43089       ],
43090       [
43091         "Mozambique (Moçambique)",
43092         "mz",
43093         "258"
43094       ],
43095       [
43096         "Myanmar (Burma) (မြန်မာ)",
43097         "mm",
43098         "95"
43099       ],
43100       [
43101         "Namibia (Namibië)",
43102         "na",
43103         "264"
43104       ],
43105       [
43106         "Nauru",
43107         "nr",
43108         "674"
43109       ],
43110       [
43111         "Nepal (नेपाल)",
43112         "np",
43113         "977"
43114       ],
43115       [
43116         "Netherlands (Nederland)",
43117         "nl",
43118         "31"
43119       ],
43120       [
43121         "New Caledonia (Nouvelle-Calédonie)",
43122         "nc",
43123         "687"
43124       ],
43125       [
43126         "New Zealand",
43127         "nz",
43128         "64"
43129       ],
43130       [
43131         "Nicaragua",
43132         "ni",
43133         "505"
43134       ],
43135       [
43136         "Niger (Nijar)",
43137         "ne",
43138         "227"
43139       ],
43140       [
43141         "Nigeria",
43142         "ng",
43143         "234"
43144       ],
43145       [
43146         "Niue",
43147         "nu",
43148         "683"
43149       ],
43150       [
43151         "Norfolk Island",
43152         "nf",
43153         "672"
43154       ],
43155       [
43156         "North Korea (조선 민주주의 인민 공화국)",
43157         "kp",
43158         "850"
43159       ],
43160       [
43161         "Northern Mariana Islands",
43162         "mp",
43163         "1670"
43164       ],
43165       [
43166         "Norway (Norge)",
43167         "no",
43168         "47",
43169         0
43170       ],
43171       [
43172         "Oman (‫عُمان‬‎)",
43173         "om",
43174         "968"
43175       ],
43176       [
43177         "Pakistan (‫پاکستان‬‎)",
43178         "pk",
43179         "92"
43180       ],
43181       [
43182         "Palau",
43183         "pw",
43184         "680"
43185       ],
43186       [
43187         "Palestine (‫فلسطين‬‎)",
43188         "ps",
43189         "970"
43190       ],
43191       [
43192         "Panama (Panamá)",
43193         "pa",
43194         "507"
43195       ],
43196       [
43197         "Papua New Guinea",
43198         "pg",
43199         "675"
43200       ],
43201       [
43202         "Paraguay",
43203         "py",
43204         "595"
43205       ],
43206       [
43207         "Peru (Perú)",
43208         "pe",
43209         "51"
43210       ],
43211       [
43212         "Philippines",
43213         "ph",
43214         "63"
43215       ],
43216       [
43217         "Poland (Polska)",
43218         "pl",
43219         "48"
43220       ],
43221       [
43222         "Portugal",
43223         "pt",
43224         "351"
43225       ],
43226       [
43227         "Puerto Rico",
43228         "pr",
43229         "1",
43230         3,
43231         ["787", "939"]
43232       ],
43233       [
43234         "Qatar (‫قطر‬‎)",
43235         "qa",
43236         "974"
43237       ],
43238       [
43239         "Réunion (La Réunion)",
43240         "re",
43241         "262",
43242         0
43243       ],
43244       [
43245         "Romania (România)",
43246         "ro",
43247         "40"
43248       ],
43249       [
43250         "Russia (Россия)",
43251         "ru",
43252         "7",
43253         0
43254       ],
43255       [
43256         "Rwanda",
43257         "rw",
43258         "250"
43259       ],
43260       [
43261         "Saint Barthélemy",
43262         "bl",
43263         "590",
43264         1
43265       ],
43266       [
43267         "Saint Helena",
43268         "sh",
43269         "290"
43270       ],
43271       [
43272         "Saint Kitts and Nevis",
43273         "kn",
43274         "1869"
43275       ],
43276       [
43277         "Saint Lucia",
43278         "lc",
43279         "1758"
43280       ],
43281       [
43282         "Saint Martin (Saint-Martin (partie française))",
43283         "mf",
43284         "590",
43285         2
43286       ],
43287       [
43288         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43289         "pm",
43290         "508"
43291       ],
43292       [
43293         "Saint Vincent and the Grenadines",
43294         "vc",
43295         "1784"
43296       ],
43297       [
43298         "Samoa",
43299         "ws",
43300         "685"
43301       ],
43302       [
43303         "San Marino",
43304         "sm",
43305         "378"
43306       ],
43307       [
43308         "São Tomé and Príncipe (São Tomé e Príncipe)",
43309         "st",
43310         "239"
43311       ],
43312       [
43313         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43314         "sa",
43315         "966"
43316       ],
43317       [
43318         "Senegal (Sénégal)",
43319         "sn",
43320         "221"
43321       ],
43322       [
43323         "Serbia (Србија)",
43324         "rs",
43325         "381"
43326       ],
43327       [
43328         "Seychelles",
43329         "sc",
43330         "248"
43331       ],
43332       [
43333         "Sierra Leone",
43334         "sl",
43335         "232"
43336       ],
43337       [
43338         "Singapore",
43339         "sg",
43340         "65"
43341       ],
43342       [
43343         "Sint Maarten",
43344         "sx",
43345         "1721"
43346       ],
43347       [
43348         "Slovakia (Slovensko)",
43349         "sk",
43350         "421"
43351       ],
43352       [
43353         "Slovenia (Slovenija)",
43354         "si",
43355         "386"
43356       ],
43357       [
43358         "Solomon Islands",
43359         "sb",
43360         "677"
43361       ],
43362       [
43363         "Somalia (Soomaaliya)",
43364         "so",
43365         "252"
43366       ],
43367       [
43368         "South Africa",
43369         "za",
43370         "27"
43371       ],
43372       [
43373         "South Korea (대한민국)",
43374         "kr",
43375         "82"
43376       ],
43377       [
43378         "South Sudan (‫جنوب السودان‬‎)",
43379         "ss",
43380         "211"
43381       ],
43382       [
43383         "Spain (España)",
43384         "es",
43385         "34"
43386       ],
43387       [
43388         "Sri Lanka (ශ්‍රී ලංකාව)",
43389         "lk",
43390         "94"
43391       ],
43392       [
43393         "Sudan (‫السودان‬‎)",
43394         "sd",
43395         "249"
43396       ],
43397       [
43398         "Suriname",
43399         "sr",
43400         "597"
43401       ],
43402       [
43403         "Svalbard and Jan Mayen",
43404         "sj",
43405         "47",
43406         1
43407       ],
43408       [
43409         "Swaziland",
43410         "sz",
43411         "268"
43412       ],
43413       [
43414         "Sweden (Sverige)",
43415         "se",
43416         "46"
43417       ],
43418       [
43419         "Switzerland (Schweiz)",
43420         "ch",
43421         "41"
43422       ],
43423       [
43424         "Syria (‫سوريا‬‎)",
43425         "sy",
43426         "963"
43427       ],
43428       [
43429         "Taiwan (台灣)",
43430         "tw",
43431         "886"
43432       ],
43433       [
43434         "Tajikistan",
43435         "tj",
43436         "992"
43437       ],
43438       [
43439         "Tanzania",
43440         "tz",
43441         "255"
43442       ],
43443       [
43444         "Thailand (ไทย)",
43445         "th",
43446         "66"
43447       ],
43448       [
43449         "Timor-Leste",
43450         "tl",
43451         "670"
43452       ],
43453       [
43454         "Togo",
43455         "tg",
43456         "228"
43457       ],
43458       [
43459         "Tokelau",
43460         "tk",
43461         "690"
43462       ],
43463       [
43464         "Tonga",
43465         "to",
43466         "676"
43467       ],
43468       [
43469         "Trinidad and Tobago",
43470         "tt",
43471         "1868"
43472       ],
43473       [
43474         "Tunisia (‫تونس‬‎)",
43475         "tn",
43476         "216"
43477       ],
43478       [
43479         "Turkey (Türkiye)",
43480         "tr",
43481         "90"
43482       ],
43483       [
43484         "Turkmenistan",
43485         "tm",
43486         "993"
43487       ],
43488       [
43489         "Turks and Caicos Islands",
43490         "tc",
43491         "1649"
43492       ],
43493       [
43494         "Tuvalu",
43495         "tv",
43496         "688"
43497       ],
43498       [
43499         "U.S. Virgin Islands",
43500         "vi",
43501         "1340"
43502       ],
43503       [
43504         "Uganda",
43505         "ug",
43506         "256"
43507       ],
43508       [
43509         "Ukraine (Україна)",
43510         "ua",
43511         "380"
43512       ],
43513       [
43514         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43515         "ae",
43516         "971"
43517       ],
43518       [
43519         "United Kingdom",
43520         "gb",
43521         "44",
43522         0
43523       ],
43524       [
43525         "United States",
43526         "us",
43527         "1",
43528         0
43529       ],
43530       [
43531         "Uruguay",
43532         "uy",
43533         "598"
43534       ],
43535       [
43536         "Uzbekistan (Oʻzbekiston)",
43537         "uz",
43538         "998"
43539       ],
43540       [
43541         "Vanuatu",
43542         "vu",
43543         "678"
43544       ],
43545       [
43546         "Vatican City (Città del Vaticano)",
43547         "va",
43548         "39",
43549         1
43550       ],
43551       [
43552         "Venezuela",
43553         "ve",
43554         "58"
43555       ],
43556       [
43557         "Vietnam (Việt Nam)",
43558         "vn",
43559         "84"
43560       ],
43561       [
43562         "Wallis and Futuna (Wallis-et-Futuna)",
43563         "wf",
43564         "681"
43565       ],
43566       [
43567         "Western Sahara (‫الصحراء الغربية‬‎)",
43568         "eh",
43569         "212",
43570         1
43571       ],
43572       [
43573         "Yemen (‫اليمن‬‎)",
43574         "ye",
43575         "967"
43576       ],
43577       [
43578         "Zambia",
43579         "zm",
43580         "260"
43581       ],
43582       [
43583         "Zimbabwe",
43584         "zw",
43585         "263"
43586       ],
43587       [
43588         "Åland Islands",
43589         "ax",
43590         "358",
43591         1
43592       ]
43593   ];
43594   
43595   return d;
43596 }/**
43597 *    This script refer to:
43598 *    Title: International Telephone Input
43599 *    Author: Jack O'Connor
43600 *    Code version:  v12.1.12
43601 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43602 **/
43603
43604 /**
43605  * @class Roo.bootstrap.PhoneInput
43606  * @extends Roo.bootstrap.TriggerField
43607  * An input with International dial-code selection
43608  
43609  * @cfg {String} defaultDialCode default '+852'
43610  * @cfg {Array} preferedCountries default []
43611   
43612  * @constructor
43613  * Create a new PhoneInput.
43614  * @param {Object} config Configuration options
43615  */
43616
43617 Roo.bootstrap.PhoneInput = function(config) {
43618     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43619 };
43620
43621 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43622         
43623         listWidth: undefined,
43624         
43625         selectedClass: 'active',
43626         
43627         invalidClass : "has-warning",
43628         
43629         validClass: 'has-success',
43630         
43631         allowed: '0123456789',
43632         
43633         max_length: 15,
43634         
43635         /**
43636          * @cfg {String} defaultDialCode The default dial code when initializing the input
43637          */
43638         defaultDialCode: '+852',
43639         
43640         /**
43641          * @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
43642          */
43643         preferedCountries: false,
43644         
43645         getAutoCreate : function()
43646         {
43647             var data = Roo.bootstrap.PhoneInputData();
43648             var align = this.labelAlign || this.parentLabelAlign();
43649             var id = Roo.id();
43650             
43651             this.allCountries = [];
43652             this.dialCodeMapping = [];
43653             
43654             for (var i = 0; i < data.length; i++) {
43655               var c = data[i];
43656               this.allCountries[i] = {
43657                 name: c[0],
43658                 iso2: c[1],
43659                 dialCode: c[2],
43660                 priority: c[3] || 0,
43661                 areaCodes: c[4] || null
43662               };
43663               this.dialCodeMapping[c[2]] = {
43664                   name: c[0],
43665                   iso2: c[1],
43666                   priority: c[3] || 0,
43667                   areaCodes: c[4] || null
43668               };
43669             }
43670             
43671             var cfg = {
43672                 cls: 'form-group',
43673                 cn: []
43674             };
43675             
43676             var input =  {
43677                 tag: 'input',
43678                 id : id,
43679                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43680                 maxlength: this.max_length,
43681                 cls : 'form-control tel-input',
43682                 autocomplete: 'new-password'
43683             };
43684             
43685             var hiddenInput = {
43686                 tag: 'input',
43687                 type: 'hidden',
43688                 cls: 'hidden-tel-input'
43689             };
43690             
43691             if (this.name) {
43692                 hiddenInput.name = this.name;
43693             }
43694             
43695             if (this.disabled) {
43696                 input.disabled = true;
43697             }
43698             
43699             var flag_container = {
43700                 tag: 'div',
43701                 cls: 'flag-box',
43702                 cn: [
43703                     {
43704                         tag: 'div',
43705                         cls: 'flag'
43706                     },
43707                     {
43708                         tag: 'div',
43709                         cls: 'caret'
43710                     }
43711                 ]
43712             };
43713             
43714             var box = {
43715                 tag: 'div',
43716                 cls: this.hasFeedback ? 'has-feedback' : '',
43717                 cn: [
43718                     hiddenInput,
43719                     input,
43720                     {
43721                         tag: 'input',
43722                         cls: 'dial-code-holder',
43723                         disabled: true
43724                     }
43725                 ]
43726             };
43727             
43728             var container = {
43729                 cls: 'roo-select2-container input-group',
43730                 cn: [
43731                     flag_container,
43732                     box
43733                 ]
43734             };
43735             
43736             if (this.fieldLabel.length) {
43737                 var indicator = {
43738                     tag: 'i',
43739                     tooltip: 'This field is required'
43740                 };
43741                 
43742                 var label = {
43743                     tag: 'label',
43744                     'for':  id,
43745                     cls: 'control-label',
43746                     cn: []
43747                 };
43748                 
43749                 var label_text = {
43750                     tag: 'span',
43751                     html: this.fieldLabel
43752                 };
43753                 
43754                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43755                 label.cn = [
43756                     indicator,
43757                     label_text
43758                 ];
43759                 
43760                 if(this.indicatorpos == 'right') {
43761                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43762                     label.cn = [
43763                         label_text,
43764                         indicator
43765                     ];
43766                 }
43767                 
43768                 if(align == 'left') {
43769                     container = {
43770                         tag: 'div',
43771                         cn: [
43772                             container
43773                         ]
43774                     };
43775                     
43776                     if(this.labelWidth > 12){
43777                         label.style = "width: " + this.labelWidth + 'px';
43778                     }
43779                     if(this.labelWidth < 13 && this.labelmd == 0){
43780                         this.labelmd = this.labelWidth;
43781                     }
43782                     if(this.labellg > 0){
43783                         label.cls += ' col-lg-' + this.labellg;
43784                         input.cls += ' col-lg-' + (12 - this.labellg);
43785                     }
43786                     if(this.labelmd > 0){
43787                         label.cls += ' col-md-' + this.labelmd;
43788                         container.cls += ' col-md-' + (12 - this.labelmd);
43789                     }
43790                     if(this.labelsm > 0){
43791                         label.cls += ' col-sm-' + this.labelsm;
43792                         container.cls += ' col-sm-' + (12 - this.labelsm);
43793                     }
43794                     if(this.labelxs > 0){
43795                         label.cls += ' col-xs-' + this.labelxs;
43796                         container.cls += ' col-xs-' + (12 - this.labelxs);
43797                     }
43798                 }
43799             }
43800             
43801             cfg.cn = [
43802                 label,
43803                 container
43804             ];
43805             
43806             var settings = this;
43807             
43808             ['xs','sm','md','lg'].map(function(size){
43809                 if (settings[size]) {
43810                     cfg.cls += ' col-' + size + '-' + settings[size];
43811                 }
43812             });
43813             
43814             this.store = new Roo.data.Store({
43815                 proxy : new Roo.data.MemoryProxy({}),
43816                 reader : new Roo.data.JsonReader({
43817                     fields : [
43818                         {
43819                             'name' : 'name',
43820                             'type' : 'string'
43821                         },
43822                         {
43823                             'name' : 'iso2',
43824                             'type' : 'string'
43825                         },
43826                         {
43827                             'name' : 'dialCode',
43828                             'type' : 'string'
43829                         },
43830                         {
43831                             'name' : 'priority',
43832                             'type' : 'string'
43833                         },
43834                         {
43835                             'name' : 'areaCodes',
43836                             'type' : 'string'
43837                         }
43838                     ]
43839                 })
43840             });
43841             
43842             if(!this.preferedCountries) {
43843                 this.preferedCountries = [
43844                     'hk',
43845                     'gb',
43846                     'us'
43847                 ];
43848             }
43849             
43850             var p = this.preferedCountries.reverse();
43851             
43852             if(p) {
43853                 for (var i = 0; i < p.length; i++) {
43854                     for (var j = 0; j < this.allCountries.length; j++) {
43855                         if(this.allCountries[j].iso2 == p[i]) {
43856                             var t = this.allCountries[j];
43857                             this.allCountries.splice(j,1);
43858                             this.allCountries.unshift(t);
43859                         }
43860                     } 
43861                 }
43862             }
43863             
43864             this.store.proxy.data = {
43865                 success: true,
43866                 data: this.allCountries
43867             };
43868             
43869             return cfg;
43870         },
43871         
43872         initEvents : function()
43873         {
43874             this.createList();
43875             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43876             
43877             this.indicator = this.indicatorEl();
43878             this.flag = this.flagEl();
43879             this.dialCodeHolder = this.dialCodeHolderEl();
43880             
43881             this.trigger = this.el.select('div.flag-box',true).first();
43882             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43883             
43884             var _this = this;
43885             
43886             (function(){
43887                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43888                 _this.list.setWidth(lw);
43889             }).defer(100);
43890             
43891             this.list.on('mouseover', this.onViewOver, this);
43892             this.list.on('mousemove', this.onViewMove, this);
43893             this.inputEl().on("keyup", this.onKeyUp, this);
43894             this.inputEl().on("keypress", this.onKeyPress, this);
43895             
43896             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43897
43898             this.view = new Roo.View(this.list, this.tpl, {
43899                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43900             });
43901             
43902             this.view.on('click', this.onViewClick, this);
43903             this.setValue(this.defaultDialCode);
43904         },
43905         
43906         onTriggerClick : function(e)
43907         {
43908             Roo.log('trigger click');
43909             if(this.disabled){
43910                 return;
43911             }
43912             
43913             if(this.isExpanded()){
43914                 this.collapse();
43915                 this.hasFocus = false;
43916             }else {
43917                 this.store.load({});
43918                 this.hasFocus = true;
43919                 this.expand();
43920             }
43921         },
43922         
43923         isExpanded : function()
43924         {
43925             return this.list.isVisible();
43926         },
43927         
43928         collapse : function()
43929         {
43930             if(!this.isExpanded()){
43931                 return;
43932             }
43933             this.list.hide();
43934             Roo.get(document).un('mousedown', this.collapseIf, this);
43935             Roo.get(document).un('mousewheel', this.collapseIf, this);
43936             this.fireEvent('collapse', this);
43937             this.validate();
43938         },
43939         
43940         expand : function()
43941         {
43942             Roo.log('expand');
43943
43944             if(this.isExpanded() || !this.hasFocus){
43945                 return;
43946             }
43947             
43948             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43949             this.list.setWidth(lw);
43950             
43951             this.list.show();
43952             this.restrictHeight();
43953             
43954             Roo.get(document).on('mousedown', this.collapseIf, this);
43955             Roo.get(document).on('mousewheel', this.collapseIf, this);
43956             
43957             this.fireEvent('expand', this);
43958         },
43959         
43960         restrictHeight : function()
43961         {
43962             this.list.alignTo(this.inputEl(), this.listAlign);
43963             this.list.alignTo(this.inputEl(), this.listAlign);
43964         },
43965         
43966         onViewOver : function(e, t)
43967         {
43968             if(this.inKeyMode){
43969                 return;
43970             }
43971             var item = this.view.findItemFromChild(t);
43972             
43973             if(item){
43974                 var index = this.view.indexOf(item);
43975                 this.select(index, false);
43976             }
43977         },
43978
43979         // private
43980         onViewClick : function(view, doFocus, el, e)
43981         {
43982             var index = this.view.getSelectedIndexes()[0];
43983             
43984             var r = this.store.getAt(index);
43985             
43986             if(r){
43987                 this.onSelect(r, index);
43988             }
43989             if(doFocus !== false && !this.blockFocus){
43990                 this.inputEl().focus();
43991             }
43992         },
43993         
43994         onViewMove : function(e, t)
43995         {
43996             this.inKeyMode = false;
43997         },
43998         
43999         select : function(index, scrollIntoView)
44000         {
44001             this.selectedIndex = index;
44002             this.view.select(index);
44003             if(scrollIntoView !== false){
44004                 var el = this.view.getNode(index);
44005                 if(el){
44006                     this.list.scrollChildIntoView(el, false);
44007                 }
44008             }
44009         },
44010         
44011         createList : function()
44012         {
44013             this.list = Roo.get(document.body).createChild({
44014                 tag: 'ul',
44015                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44016                 style: 'display:none'
44017             });
44018             
44019             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44020         },
44021         
44022         collapseIf : function(e)
44023         {
44024             var in_combo  = e.within(this.el);
44025             var in_list =  e.within(this.list);
44026             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44027             
44028             if (in_combo || in_list || is_list) {
44029                 return;
44030             }
44031             this.collapse();
44032         },
44033         
44034         onSelect : function(record, index)
44035         {
44036             if(this.fireEvent('beforeselect', this, record, index) !== false){
44037                 
44038                 this.setFlagClass(record.data.iso2);
44039                 this.setDialCode(record.data.dialCode);
44040                 this.hasFocus = false;
44041                 this.collapse();
44042                 this.fireEvent('select', this, record, index);
44043             }
44044         },
44045         
44046         flagEl : function()
44047         {
44048             var flag = this.el.select('div.flag',true).first();
44049             if(!flag){
44050                 return false;
44051             }
44052             return flag;
44053         },
44054         
44055         dialCodeHolderEl : function()
44056         {
44057             var d = this.el.select('input.dial-code-holder',true).first();
44058             if(!d){
44059                 return false;
44060             }
44061             return d;
44062         },
44063         
44064         setDialCode : function(v)
44065         {
44066             this.dialCodeHolder.dom.value = '+'+v;
44067         },
44068         
44069         setFlagClass : function(n)
44070         {
44071             this.flag.dom.className = 'flag '+n;
44072         },
44073         
44074         getValue : function()
44075         {
44076             var v = this.inputEl().getValue();
44077             if(this.dialCodeHolder) {
44078                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44079             }
44080             return v;
44081         },
44082         
44083         setValue : function(v)
44084         {
44085             var d = this.getDialCode(v);
44086             
44087             //invalid dial code
44088             if(v.length == 0 || !d || d.length == 0) {
44089                 if(this.rendered){
44090                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44091                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44092                 }
44093                 return;
44094             }
44095             
44096             //valid dial code
44097             this.setFlagClass(this.dialCodeMapping[d].iso2);
44098             this.setDialCode(d);
44099             this.inputEl().dom.value = v.replace('+'+d,'');
44100             this.hiddenEl().dom.value = this.getValue();
44101             
44102             this.validate();
44103         },
44104         
44105         getDialCode : function(v)
44106         {
44107             v = v ||  '';
44108             
44109             if (v.length == 0) {
44110                 return this.dialCodeHolder.dom.value;
44111             }
44112             
44113             var dialCode = "";
44114             if (v.charAt(0) != "+") {
44115                 return false;
44116             }
44117             var numericChars = "";
44118             for (var i = 1; i < v.length; i++) {
44119               var c = v.charAt(i);
44120               if (!isNaN(c)) {
44121                 numericChars += c;
44122                 if (this.dialCodeMapping[numericChars]) {
44123                   dialCode = v.substr(1, i);
44124                 }
44125                 if (numericChars.length == 4) {
44126                   break;
44127                 }
44128               }
44129             }
44130             return dialCode;
44131         },
44132         
44133         reset : function()
44134         {
44135             this.setValue(this.defaultDialCode);
44136             this.validate();
44137         },
44138         
44139         hiddenEl : function()
44140         {
44141             return this.el.select('input.hidden-tel-input',true).first();
44142         },
44143         
44144         // after setting val
44145         onKeyUp : function(e){
44146             this.setValue(this.getValue());
44147         },
44148         
44149         onKeyPress : function(e){
44150             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44151                 e.stopEvent();
44152             }
44153         }
44154         
44155 });
44156 /**
44157  * @class Roo.bootstrap.MoneyField
44158  * @extends Roo.bootstrap.ComboBox
44159  * Bootstrap MoneyField class
44160  * 
44161  * @constructor
44162  * Create a new MoneyField.
44163  * @param {Object} config Configuration options
44164  */
44165
44166 Roo.bootstrap.MoneyField = function(config) {
44167     
44168     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44169     
44170 };
44171
44172 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44173     
44174     /**
44175      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44176      */
44177     allowDecimals : true,
44178     /**
44179      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44180      */
44181     decimalSeparator : ".",
44182     /**
44183      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44184      */
44185     decimalPrecision : 0,
44186     /**
44187      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44188      */
44189     allowNegative : true,
44190     /**
44191      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44192      */
44193     allowZero: true,
44194     /**
44195      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44196      */
44197     minValue : Number.NEGATIVE_INFINITY,
44198     /**
44199      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44200      */
44201     maxValue : Number.MAX_VALUE,
44202     /**
44203      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44204      */
44205     minText : "The minimum value for this field is {0}",
44206     /**
44207      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44208      */
44209     maxText : "The maximum value for this field is {0}",
44210     /**
44211      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44212      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44213      */
44214     nanText : "{0} is not a valid number",
44215     /**
44216      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44217      */
44218     castInt : true,
44219     /**
44220      * @cfg {String} defaults currency of the MoneyField
44221      * value should be in lkey
44222      */
44223     defaultCurrency : false,
44224     /**
44225      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44226      */
44227     thousandsDelimiter : false,
44228     /**
44229      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44230      */
44231     max_length: false,
44232     
44233     inputlg : 9,
44234     inputmd : 9,
44235     inputsm : 9,
44236     inputxs : 6,
44237     
44238     store : false,
44239     
44240     getAutoCreate : function()
44241     {
44242         var align = this.labelAlign || this.parentLabelAlign();
44243         
44244         var id = Roo.id();
44245
44246         var cfg = {
44247             cls: 'form-group',
44248             cn: []
44249         };
44250
44251         var input =  {
44252             tag: 'input',
44253             id : id,
44254             cls : 'form-control roo-money-amount-input',
44255             autocomplete: 'new-password'
44256         };
44257         
44258         var hiddenInput = {
44259             tag: 'input',
44260             type: 'hidden',
44261             id: Roo.id(),
44262             cls: 'hidden-number-input'
44263         };
44264         
44265         if(this.max_length) {
44266             input.maxlength = this.max_length; 
44267         }
44268         
44269         if (this.name) {
44270             hiddenInput.name = this.name;
44271         }
44272
44273         if (this.disabled) {
44274             input.disabled = true;
44275         }
44276
44277         var clg = 12 - this.inputlg;
44278         var cmd = 12 - this.inputmd;
44279         var csm = 12 - this.inputsm;
44280         var cxs = 12 - this.inputxs;
44281         
44282         var container = {
44283             tag : 'div',
44284             cls : 'row roo-money-field',
44285             cn : [
44286                 {
44287                     tag : 'div',
44288                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44289                     cn : [
44290                         {
44291                             tag : 'div',
44292                             cls: 'roo-select2-container input-group',
44293                             cn: [
44294                                 {
44295                                     tag : 'input',
44296                                     cls : 'form-control roo-money-currency-input',
44297                                     autocomplete: 'new-password',
44298                                     readOnly : 1,
44299                                     name : this.currencyName
44300                                 },
44301                                 {
44302                                     tag :'span',
44303                                     cls : 'input-group-addon',
44304                                     cn : [
44305                                         {
44306                                             tag: 'span',
44307                                             cls: 'caret'
44308                                         }
44309                                     ]
44310                                 }
44311                             ]
44312                         }
44313                     ]
44314                 },
44315                 {
44316                     tag : 'div',
44317                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44318                     cn : [
44319                         {
44320                             tag: 'div',
44321                             cls: this.hasFeedback ? 'has-feedback' : '',
44322                             cn: [
44323                                 input
44324                             ]
44325                         }
44326                     ]
44327                 }
44328             ]
44329             
44330         };
44331         
44332         if (this.fieldLabel.length) {
44333             var indicator = {
44334                 tag: 'i',
44335                 tooltip: 'This field is required'
44336             };
44337
44338             var label = {
44339                 tag: 'label',
44340                 'for':  id,
44341                 cls: 'control-label',
44342                 cn: []
44343             };
44344
44345             var label_text = {
44346                 tag: 'span',
44347                 html: this.fieldLabel
44348             };
44349
44350             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44351             label.cn = [
44352                 indicator,
44353                 label_text
44354             ];
44355
44356             if(this.indicatorpos == 'right') {
44357                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44358                 label.cn = [
44359                     label_text,
44360                     indicator
44361                 ];
44362             }
44363
44364             if(align == 'left') {
44365                 container = {
44366                     tag: 'div',
44367                     cn: [
44368                         container
44369                     ]
44370                 };
44371
44372                 if(this.labelWidth > 12){
44373                     label.style = "width: " + this.labelWidth + 'px';
44374                 }
44375                 if(this.labelWidth < 13 && this.labelmd == 0){
44376                     this.labelmd = this.labelWidth;
44377                 }
44378                 if(this.labellg > 0){
44379                     label.cls += ' col-lg-' + this.labellg;
44380                     input.cls += ' col-lg-' + (12 - this.labellg);
44381                 }
44382                 if(this.labelmd > 0){
44383                     label.cls += ' col-md-' + this.labelmd;
44384                     container.cls += ' col-md-' + (12 - this.labelmd);
44385                 }
44386                 if(this.labelsm > 0){
44387                     label.cls += ' col-sm-' + this.labelsm;
44388                     container.cls += ' col-sm-' + (12 - this.labelsm);
44389                 }
44390                 if(this.labelxs > 0){
44391                     label.cls += ' col-xs-' + this.labelxs;
44392                     container.cls += ' col-xs-' + (12 - this.labelxs);
44393                 }
44394             }
44395         }
44396
44397         cfg.cn = [
44398             label,
44399             container,
44400             hiddenInput
44401         ];
44402         
44403         var settings = this;
44404
44405         ['xs','sm','md','lg'].map(function(size){
44406             if (settings[size]) {
44407                 cfg.cls += ' col-' + size + '-' + settings[size];
44408             }
44409         });
44410         
44411         return cfg;
44412     },
44413     
44414     initEvents : function()
44415     {
44416         this.indicator = this.indicatorEl();
44417         
44418         this.initCurrencyEvent();
44419         
44420         this.initNumberEvent();
44421     },
44422     
44423     initCurrencyEvent : function()
44424     {
44425         if (!this.store) {
44426             throw "can not find store for combo";
44427         }
44428         
44429         this.store = Roo.factory(this.store, Roo.data);
44430         this.store.parent = this;
44431         
44432         this.createList();
44433         
44434         this.triggerEl = this.el.select('.input-group-addon', true).first();
44435         
44436         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44437         
44438         var _this = this;
44439         
44440         (function(){
44441             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44442             _this.list.setWidth(lw);
44443         }).defer(100);
44444         
44445         this.list.on('mouseover', this.onViewOver, this);
44446         this.list.on('mousemove', this.onViewMove, this);
44447         this.list.on('scroll', this.onViewScroll, this);
44448         
44449         if(!this.tpl){
44450             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44451         }
44452         
44453         this.view = new Roo.View(this.list, this.tpl, {
44454             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44455         });
44456         
44457         this.view.on('click', this.onViewClick, this);
44458         
44459         this.store.on('beforeload', this.onBeforeLoad, this);
44460         this.store.on('load', this.onLoad, this);
44461         this.store.on('loadexception', this.onLoadException, this);
44462         
44463         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44464             "up" : function(e){
44465                 this.inKeyMode = true;
44466                 this.selectPrev();
44467             },
44468
44469             "down" : function(e){
44470                 if(!this.isExpanded()){
44471                     this.onTriggerClick();
44472                 }else{
44473                     this.inKeyMode = true;
44474                     this.selectNext();
44475                 }
44476             },
44477
44478             "enter" : function(e){
44479                 this.collapse();
44480                 
44481                 if(this.fireEvent("specialkey", this, e)){
44482                     this.onViewClick(false);
44483                 }
44484                 
44485                 return true;
44486             },
44487
44488             "esc" : function(e){
44489                 this.collapse();
44490             },
44491
44492             "tab" : function(e){
44493                 this.collapse();
44494                 
44495                 if(this.fireEvent("specialkey", this, e)){
44496                     this.onViewClick(false);
44497                 }
44498                 
44499                 return true;
44500             },
44501
44502             scope : this,
44503
44504             doRelay : function(foo, bar, hname){
44505                 if(hname == 'down' || this.scope.isExpanded()){
44506                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44507                 }
44508                 return true;
44509             },
44510
44511             forceKeyDown: true
44512         });
44513         
44514         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44515         
44516     },
44517     
44518     initNumberEvent : function(e)
44519     {
44520         this.inputEl().on("keydown" , this.fireKey,  this);
44521         this.inputEl().on("focus", this.onFocus,  this);
44522         this.inputEl().on("blur", this.onBlur,  this);
44523         
44524         this.inputEl().relayEvent('keyup', this);
44525         
44526         if(this.indicator){
44527             this.indicator.addClass('invisible');
44528         }
44529  
44530         this.originalValue = this.getValue();
44531         
44532         if(this.validationEvent == 'keyup'){
44533             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44534             this.inputEl().on('keyup', this.filterValidation, this);
44535         }
44536         else if(this.validationEvent !== false){
44537             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44538         }
44539         
44540         if(this.selectOnFocus){
44541             this.on("focus", this.preFocus, this);
44542             
44543         }
44544         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44545             this.inputEl().on("keypress", this.filterKeys, this);
44546         } else {
44547             this.inputEl().relayEvent('keypress', this);
44548         }
44549         
44550         var allowed = "0123456789";
44551         
44552         if(this.allowDecimals){
44553             allowed += this.decimalSeparator;
44554         }
44555         
44556         if(this.allowNegative){
44557             allowed += "-";
44558         }
44559         
44560         if(this.thousandsDelimiter) {
44561             allowed += ",";
44562         }
44563         
44564         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44565         
44566         var keyPress = function(e){
44567             
44568             var k = e.getKey();
44569             
44570             var c = e.getCharCode();
44571             
44572             if(
44573                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44574                     allowed.indexOf(String.fromCharCode(c)) === -1
44575             ){
44576                 e.stopEvent();
44577                 return;
44578             }
44579             
44580             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44581                 return;
44582             }
44583             
44584             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44585                 e.stopEvent();
44586             }
44587         };
44588         
44589         this.inputEl().on("keypress", keyPress, this);
44590         
44591     },
44592     
44593     onTriggerClick : function(e)
44594     {   
44595         if(this.disabled){
44596             return;
44597         }
44598         
44599         this.page = 0;
44600         this.loadNext = false;
44601         
44602         if(this.isExpanded()){
44603             this.collapse();
44604             return;
44605         }
44606         
44607         this.hasFocus = true;
44608         
44609         if(this.triggerAction == 'all') {
44610             this.doQuery(this.allQuery, true);
44611             return;
44612         }
44613         
44614         this.doQuery(this.getRawValue());
44615     },
44616     
44617     getCurrency : function()
44618     {   
44619         var v = this.currencyEl().getValue();
44620         
44621         return v;
44622     },
44623     
44624     restrictHeight : function()
44625     {
44626         this.list.alignTo(this.currencyEl(), this.listAlign);
44627         this.list.alignTo(this.currencyEl(), this.listAlign);
44628     },
44629     
44630     onViewClick : function(view, doFocus, el, e)
44631     {
44632         var index = this.view.getSelectedIndexes()[0];
44633         
44634         var r = this.store.getAt(index);
44635         
44636         if(r){
44637             this.onSelect(r, index);
44638         }
44639     },
44640     
44641     onSelect : function(record, index){
44642         
44643         if(this.fireEvent('beforeselect', this, record, index) !== false){
44644         
44645             this.setFromCurrencyData(index > -1 ? record.data : false);
44646             
44647             this.collapse();
44648             
44649             this.fireEvent('select', this, record, index);
44650         }
44651     },
44652     
44653     setFromCurrencyData : function(o)
44654     {
44655         var currency = '';
44656         
44657         this.lastCurrency = o;
44658         
44659         if (this.currencyField) {
44660             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44661         } else {
44662             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44663         }
44664         
44665         this.lastSelectionText = currency;
44666         
44667         //setting default currency
44668         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44669             this.setCurrency(this.defaultCurrency);
44670             return;
44671         }
44672         
44673         this.setCurrency(currency);
44674     },
44675     
44676     setFromData : function(o)
44677     {
44678         var c = {};
44679         
44680         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44681         
44682         this.setFromCurrencyData(c);
44683         
44684         var value = '';
44685         
44686         if (this.name) {
44687             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44688         } else {
44689             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44690         }
44691         
44692         this.setValue(value);
44693         
44694     },
44695     
44696     setCurrency : function(v)
44697     {   
44698         this.currencyValue = v;
44699         
44700         if(this.rendered){
44701             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44702             this.validate();
44703         }
44704     },
44705     
44706     setValue : function(v)
44707     {
44708         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44709         
44710         this.value = v;
44711         
44712         if(this.rendered){
44713             
44714             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44715             
44716             this.inputEl().dom.value = (v == '') ? '' :
44717                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44718             
44719             if(!this.allowZero && v === '0') {
44720                 this.hiddenEl().dom.value = '';
44721                 this.inputEl().dom.value = '';
44722             }
44723             
44724             this.validate();
44725         }
44726     },
44727     
44728     getRawValue : function()
44729     {
44730         var v = this.inputEl().getValue();
44731         
44732         return v;
44733     },
44734     
44735     getValue : function()
44736     {
44737         return this.fixPrecision(this.parseValue(this.getRawValue()));
44738     },
44739     
44740     parseValue : function(value)
44741     {
44742         if(this.thousandsDelimiter) {
44743             value += "";
44744             r = new RegExp(",", "g");
44745             value = value.replace(r, "");
44746         }
44747         
44748         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44749         return isNaN(value) ? '' : value;
44750         
44751     },
44752     
44753     fixPrecision : function(value)
44754     {
44755         if(this.thousandsDelimiter) {
44756             value += "";
44757             r = new RegExp(",", "g");
44758             value = value.replace(r, "");
44759         }
44760         
44761         var nan = isNaN(value);
44762         
44763         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44764             return nan ? '' : value;
44765         }
44766         return parseFloat(value).toFixed(this.decimalPrecision);
44767     },
44768     
44769     decimalPrecisionFcn : function(v)
44770     {
44771         return Math.floor(v);
44772     },
44773     
44774     validateValue : function(value)
44775     {
44776         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44777             return false;
44778         }
44779         
44780         var num = this.parseValue(value);
44781         
44782         if(isNaN(num)){
44783             this.markInvalid(String.format(this.nanText, value));
44784             return false;
44785         }
44786         
44787         if(num < this.minValue){
44788             this.markInvalid(String.format(this.minText, this.minValue));
44789             return false;
44790         }
44791         
44792         if(num > this.maxValue){
44793             this.markInvalid(String.format(this.maxText, this.maxValue));
44794             return false;
44795         }
44796         
44797         return true;
44798     },
44799     
44800     validate : function()
44801     {
44802         if(this.disabled || this.allowBlank){
44803             this.markValid();
44804             return true;
44805         }
44806         
44807         var currency = this.getCurrency();
44808         
44809         if(this.validateValue(this.getRawValue()) && currency.length){
44810             this.markValid();
44811             return true;
44812         }
44813         
44814         this.markInvalid();
44815         return false;
44816     },
44817     
44818     getName: function()
44819     {
44820         return this.name;
44821     },
44822     
44823     beforeBlur : function()
44824     {
44825         if(!this.castInt){
44826             return;
44827         }
44828         
44829         var v = this.parseValue(this.getRawValue());
44830         
44831         if(v || v == 0){
44832             this.setValue(v);
44833         }
44834     },
44835     
44836     onBlur : function()
44837     {
44838         this.beforeBlur();
44839         
44840         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44841             //this.el.removeClass(this.focusClass);
44842         }
44843         
44844         this.hasFocus = false;
44845         
44846         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44847             this.validate();
44848         }
44849         
44850         var v = this.getValue();
44851         
44852         if(String(v) !== String(this.startValue)){
44853             this.fireEvent('change', this, v, this.startValue);
44854         }
44855         
44856         this.fireEvent("blur", this);
44857     },
44858     
44859     inputEl : function()
44860     {
44861         return this.el.select('.roo-money-amount-input', true).first();
44862     },
44863     
44864     currencyEl : function()
44865     {
44866         return this.el.select('.roo-money-currency-input', true).first();
44867     },
44868     
44869     hiddenEl : function()
44870     {
44871         return this.el.select('input.hidden-number-input',true).first();
44872     }
44873     
44874 });/**
44875  * @class Roo.bootstrap.BezierSignature
44876  * @extends Roo.bootstrap.Component
44877  * Bootstrap BezierSignature class
44878  * This script refer to:
44879  *    Title: Signature Pad
44880  *    Author: szimek
44881  *    Availability: https://github.com/szimek/signature_pad
44882  *
44883  * @constructor
44884  * Create a new BezierSignature
44885  * @param {Object} config The config object
44886  */
44887
44888 Roo.bootstrap.BezierSignature = function(config){
44889     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44890     this.addEvents({
44891         "resize" : true
44892     });
44893 };
44894
44895 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44896 {
44897      
44898     curve_data: [],
44899     
44900     is_empty: true,
44901     
44902     mouse_btn_down: true,
44903     
44904     /**
44905      * @cfg {int} canvas height
44906      */
44907     canvas_height: '200px',
44908     
44909     /**
44910      * @cfg {float|function} Radius of a single dot.
44911      */ 
44912     dot_size: false,
44913     
44914     /**
44915      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44916      */
44917     min_width: 0.5,
44918     
44919     /**
44920      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44921      */
44922     max_width: 2.5,
44923     
44924     /**
44925      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44926      */
44927     throttle: 16,
44928     
44929     /**
44930      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44931      */
44932     min_distance: 5,
44933     
44934     /**
44935      * @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.
44936      */
44937     bg_color: 'rgba(0, 0, 0, 0)',
44938     
44939     /**
44940      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44941      */
44942     dot_color: 'black',
44943     
44944     /**
44945      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44946      */ 
44947     velocity_filter_weight: 0.7,
44948     
44949     /**
44950      * @cfg {function} Callback when stroke begin. 
44951      */
44952     onBegin: false,
44953     
44954     /**
44955      * @cfg {function} Callback when stroke end.
44956      */
44957     onEnd: false,
44958     
44959     getAutoCreate : function()
44960     {
44961         var cls = 'roo-signature column';
44962         
44963         if(this.cls){
44964             cls += ' ' + this.cls;
44965         }
44966         
44967         var col_sizes = [
44968             'lg',
44969             'md',
44970             'sm',
44971             'xs'
44972         ];
44973         
44974         for(var i = 0; i < col_sizes.length; i++) {
44975             if(this[col_sizes[i]]) {
44976                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44977             }
44978         }
44979         
44980         var cfg = {
44981             tag: 'div',
44982             cls: cls,
44983             cn: [
44984                 {
44985                     tag: 'div',
44986                     cls: 'roo-signature-body',
44987                     cn: [
44988                         {
44989                             tag: 'canvas',
44990                             cls: 'roo-signature-body-canvas',
44991                             height: this.canvas_height,
44992                             width: this.canvas_width
44993                         }
44994                     ]
44995                 },
44996                 {
44997                     tag: 'input',
44998                     type: 'file',
44999                     style: 'display: none'
45000                 }
45001             ]
45002         };
45003         
45004         return cfg;
45005     },
45006     
45007     initEvents: function() 
45008     {
45009         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45010         
45011         var canvas = this.canvasEl();
45012         
45013         // mouse && touch event swapping...
45014         canvas.dom.style.touchAction = 'none';
45015         canvas.dom.style.msTouchAction = 'none';
45016         
45017         this.mouse_btn_down = false;
45018         canvas.on('mousedown', this._handleMouseDown, this);
45019         canvas.on('mousemove', this._handleMouseMove, this);
45020         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45021         
45022         if (window.PointerEvent) {
45023             canvas.on('pointerdown', this._handleMouseDown, this);
45024             canvas.on('pointermove', this._handleMouseMove, this);
45025             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45026         }
45027         
45028         if ('ontouchstart' in window) {
45029             canvas.on('touchstart', this._handleTouchStart, this);
45030             canvas.on('touchmove', this._handleTouchMove, this);
45031             canvas.on('touchend', this._handleTouchEnd, this);
45032         }
45033         
45034         Roo.EventManager.onWindowResize(this.resize, this, true);
45035         
45036         // file input event
45037         this.fileEl().on('change', this.uploadImage, this);
45038         
45039         this.clear();
45040         
45041         this.resize();
45042     },
45043     
45044     resize: function(){
45045         
45046         var canvas = this.canvasEl().dom;
45047         var ctx = this.canvasElCtx();
45048         var img_data = false;
45049         
45050         if(canvas.width > 0) {
45051             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45052         }
45053         // setting canvas width will clean img data
45054         canvas.width = 0;
45055         
45056         var style = window.getComputedStyle ? 
45057             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45058             
45059         var padding_left = parseInt(style.paddingLeft) || 0;
45060         var padding_right = parseInt(style.paddingRight) || 0;
45061         
45062         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45063         
45064         if(img_data) {
45065             ctx.putImageData(img_data, 0, 0);
45066         }
45067     },
45068     
45069     _handleMouseDown: function(e)
45070     {
45071         if (e.browserEvent.which === 1) {
45072             this.mouse_btn_down = true;
45073             this.strokeBegin(e);
45074         }
45075     },
45076     
45077     _handleMouseMove: function (e)
45078     {
45079         if (this.mouse_btn_down) {
45080             this.strokeMoveUpdate(e);
45081         }
45082     },
45083     
45084     _handleMouseUp: function (e)
45085     {
45086         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45087             this.mouse_btn_down = false;
45088             this.strokeEnd(e);
45089         }
45090     },
45091     
45092     _handleTouchStart: function (e) {
45093         
45094         e.preventDefault();
45095         if (e.browserEvent.targetTouches.length === 1) {
45096             // var touch = e.browserEvent.changedTouches[0];
45097             // this.strokeBegin(touch);
45098             
45099              this.strokeBegin(e); // assume e catching the correct xy...
45100         }
45101     },
45102     
45103     _handleTouchMove: function (e) {
45104         e.preventDefault();
45105         // var touch = event.targetTouches[0];
45106         // _this._strokeMoveUpdate(touch);
45107         this.strokeMoveUpdate(e);
45108     },
45109     
45110     _handleTouchEnd: function (e) {
45111         var wasCanvasTouched = e.target === this.canvasEl().dom;
45112         if (wasCanvasTouched) {
45113             e.preventDefault();
45114             // var touch = event.changedTouches[0];
45115             // _this._strokeEnd(touch);
45116             this.strokeEnd(e);
45117         }
45118     },
45119     
45120     reset: function () {
45121         this._lastPoints = [];
45122         this._lastVelocity = 0;
45123         this._lastWidth = (this.min_width + this.max_width) / 2;
45124         this.canvasElCtx().fillStyle = this.dot_color;
45125     },
45126     
45127     strokeMoveUpdate: function(e)
45128     {
45129         this.strokeUpdate(e);
45130         
45131         if (this.throttle) {
45132             this.throttleStroke(this.strokeUpdate, this.throttle);
45133         }
45134         else {
45135             this.strokeUpdate(e);
45136         }
45137     },
45138     
45139     strokeBegin: function(e)
45140     {
45141         var newPointGroup = {
45142             color: this.dot_color,
45143             points: []
45144         };
45145         
45146         if (typeof this.onBegin === 'function') {
45147             this.onBegin(e);
45148         }
45149         
45150         this.curve_data.push(newPointGroup);
45151         this.reset();
45152         this.strokeUpdate(e);
45153     },
45154     
45155     strokeUpdate: function(e)
45156     {
45157         var rect = this.canvasEl().dom.getBoundingClientRect();
45158         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45159         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45160         var lastPoints = lastPointGroup.points;
45161         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45162         var isLastPointTooClose = lastPoint
45163             ? point.distanceTo(lastPoint) <= this.min_distance
45164             : false;
45165         var color = lastPointGroup.color;
45166         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45167             var curve = this.addPoint(point);
45168             if (!lastPoint) {
45169                 this.drawDot({color: color, point: point});
45170             }
45171             else if (curve) {
45172                 this.drawCurve({color: color, curve: curve});
45173             }
45174             lastPoints.push({
45175                 time: point.time,
45176                 x: point.x,
45177                 y: point.y
45178             });
45179         }
45180     },
45181     
45182     strokeEnd: function(e)
45183     {
45184         this.strokeUpdate(e);
45185         if (typeof this.onEnd === 'function') {
45186             this.onEnd(e);
45187         }
45188     },
45189     
45190     addPoint:  function (point) {
45191         var _lastPoints = this._lastPoints;
45192         _lastPoints.push(point);
45193         if (_lastPoints.length > 2) {
45194             if (_lastPoints.length === 3) {
45195                 _lastPoints.unshift(_lastPoints[0]);
45196             }
45197             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45198             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45199             _lastPoints.shift();
45200             return curve;
45201         }
45202         return null;
45203     },
45204     
45205     calculateCurveWidths: function (startPoint, endPoint) {
45206         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45207             (1 - this.velocity_filter_weight) * this._lastVelocity;
45208
45209         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45210         var widths = {
45211             end: newWidth,
45212             start: this._lastWidth
45213         };
45214         
45215         this._lastVelocity = velocity;
45216         this._lastWidth = newWidth;
45217         return widths;
45218     },
45219     
45220     drawDot: function (_a) {
45221         var color = _a.color, point = _a.point;
45222         var ctx = this.canvasElCtx();
45223         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45224         ctx.beginPath();
45225         this.drawCurveSegment(point.x, point.y, width);
45226         ctx.closePath();
45227         ctx.fillStyle = color;
45228         ctx.fill();
45229     },
45230     
45231     drawCurve: function (_a) {
45232         var color = _a.color, curve = _a.curve;
45233         var ctx = this.canvasElCtx();
45234         var widthDelta = curve.endWidth - curve.startWidth;
45235         var drawSteps = Math.floor(curve.length()) * 2;
45236         ctx.beginPath();
45237         ctx.fillStyle = color;
45238         for (var i = 0; i < drawSteps; i += 1) {
45239         var t = i / drawSteps;
45240         var tt = t * t;
45241         var ttt = tt * t;
45242         var u = 1 - t;
45243         var uu = u * u;
45244         var uuu = uu * u;
45245         var x = uuu * curve.startPoint.x;
45246         x += 3 * uu * t * curve.control1.x;
45247         x += 3 * u * tt * curve.control2.x;
45248         x += ttt * curve.endPoint.x;
45249         var y = uuu * curve.startPoint.y;
45250         y += 3 * uu * t * curve.control1.y;
45251         y += 3 * u * tt * curve.control2.y;
45252         y += ttt * curve.endPoint.y;
45253         var width = curve.startWidth + ttt * widthDelta;
45254         this.drawCurveSegment(x, y, width);
45255         }
45256         ctx.closePath();
45257         ctx.fill();
45258     },
45259     
45260     drawCurveSegment: function (x, y, width) {
45261         var ctx = this.canvasElCtx();
45262         ctx.moveTo(x, y);
45263         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45264         this.is_empty = false;
45265     },
45266     
45267     clear: function()
45268     {
45269         var ctx = this.canvasElCtx();
45270         var canvas = this.canvasEl().dom;
45271         ctx.fillStyle = this.bg_color;
45272         ctx.clearRect(0, 0, canvas.width, canvas.height);
45273         ctx.fillRect(0, 0, canvas.width, canvas.height);
45274         this.curve_data = [];
45275         this.reset();
45276         this.is_empty = true;
45277     },
45278     
45279     fileEl: function()
45280     {
45281         return  this.el.select('input',true).first();
45282     },
45283     
45284     canvasEl: function()
45285     {
45286         return this.el.select('canvas',true).first();
45287     },
45288     
45289     canvasElCtx: function()
45290     {
45291         return this.el.select('canvas',true).first().dom.getContext('2d');
45292     },
45293     
45294     getImage: function(type)
45295     {
45296         if(this.is_empty) {
45297             return false;
45298         }
45299         
45300         // encryption ?
45301         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45302     },
45303     
45304     drawFromImage: function(img_src)
45305     {
45306         var img = new Image();
45307         
45308         img.onload = function(){
45309             this.canvasElCtx().drawImage(img, 0, 0);
45310         }.bind(this);
45311         
45312         img.src = img_src;
45313         
45314         this.is_empty = false;
45315     },
45316     
45317     selectImage: function()
45318     {
45319         this.fileEl().dom.click();
45320     },
45321     
45322     uploadImage: function(e)
45323     {
45324         var reader = new FileReader();
45325         
45326         reader.onload = function(e){
45327             var img = new Image();
45328             img.onload = function(){
45329                 this.reset();
45330                 this.canvasElCtx().drawImage(img, 0, 0);
45331             }.bind(this);
45332             img.src = e.target.result;
45333         }.bind(this);
45334         
45335         reader.readAsDataURL(e.target.files[0]);
45336     },
45337     
45338     // Bezier Point Constructor
45339     Point: (function () {
45340         function Point(x, y, time) {
45341             this.x = x;
45342             this.y = y;
45343             this.time = time || Date.now();
45344         }
45345         Point.prototype.distanceTo = function (start) {
45346             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45347         };
45348         Point.prototype.equals = function (other) {
45349             return this.x === other.x && this.y === other.y && this.time === other.time;
45350         };
45351         Point.prototype.velocityFrom = function (start) {
45352             return this.time !== start.time
45353             ? this.distanceTo(start) / (this.time - start.time)
45354             : 0;
45355         };
45356         return Point;
45357     }()),
45358     
45359     
45360     // Bezier Constructor
45361     Bezier: (function () {
45362         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45363             this.startPoint = startPoint;
45364             this.control2 = control2;
45365             this.control1 = control1;
45366             this.endPoint = endPoint;
45367             this.startWidth = startWidth;
45368             this.endWidth = endWidth;
45369         }
45370         Bezier.fromPoints = function (points, widths, scope) {
45371             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45372             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45373             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45374         };
45375         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45376             var dx1 = s1.x - s2.x;
45377             var dy1 = s1.y - s2.y;
45378             var dx2 = s2.x - s3.x;
45379             var dy2 = s2.y - s3.y;
45380             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45381             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45382             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45383             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45384             var dxm = m1.x - m2.x;
45385             var dym = m1.y - m2.y;
45386             var k = l2 / (l1 + l2);
45387             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45388             var tx = s2.x - cm.x;
45389             var ty = s2.y - cm.y;
45390             return {
45391                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45392                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45393             };
45394         };
45395         Bezier.prototype.length = function () {
45396             var steps = 10;
45397             var length = 0;
45398             var px;
45399             var py;
45400             for (var i = 0; i <= steps; i += 1) {
45401                 var t = i / steps;
45402                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45403                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45404                 if (i > 0) {
45405                     var xdiff = cx - px;
45406                     var ydiff = cy - py;
45407                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45408                 }
45409                 px = cx;
45410                 py = cy;
45411             }
45412             return length;
45413         };
45414         Bezier.prototype.point = function (t, start, c1, c2, end) {
45415             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45416             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45417             + (3.0 * c2 * (1.0 - t) * t * t)
45418             + (end * t * t * t);
45419         };
45420         return Bezier;
45421     }()),
45422     
45423     throttleStroke: function(fn, wait) {
45424       if (wait === void 0) { wait = 250; }
45425       var previous = 0;
45426       var timeout = null;
45427       var result;
45428       var storedContext;
45429       var storedArgs;
45430       var later = function () {
45431           previous = Date.now();
45432           timeout = null;
45433           result = fn.apply(storedContext, storedArgs);
45434           if (!timeout) {
45435               storedContext = null;
45436               storedArgs = [];
45437           }
45438       };
45439       return function wrapper() {
45440           var args = [];
45441           for (var _i = 0; _i < arguments.length; _i++) {
45442               args[_i] = arguments[_i];
45443           }
45444           var now = Date.now();
45445           var remaining = wait - (now - previous);
45446           storedContext = this;
45447           storedArgs = args;
45448           if (remaining <= 0 || remaining > wait) {
45449               if (timeout) {
45450                   clearTimeout(timeout);
45451                   timeout = null;
45452               }
45453               previous = now;
45454               result = fn.apply(storedContext, storedArgs);
45455               if (!timeout) {
45456                   storedContext = null;
45457                   storedArgs = [];
45458               }
45459           }
45460           else if (!timeout) {
45461               timeout = window.setTimeout(later, remaining);
45462           }
45463           return result;
45464       };
45465   }
45466   
45467 });
45468
45469  
45470
45471